/******************************************************************************
 * * hal_bt.c - realize functions in hal.h
 *
 * *(C) Copyright 2019 Asr International Ltd.
 * * All Rights Reserved
 * ******************************************************************************/

/*********************
 *      INCLUDES
 *********************/
#include <stdio.h>
#include <stdlib.h>

#include "lvgl/lvgl.h"
#include "hal_bt.h"

#if USE_LV_BLUETOOTH != 0
#include <ctype.h>
#include "../../../product/craneg_modem/csw/BSP/inc/firmware.h"

/*********************
 *      DEFINES
 *********************/
#define VAL_SWAP16(x)       ((((x) & 0x00ff) << 8) | ((x) >> 8))
/**********************
 *      TYPEDEFS
 **********************/

/**********************
 *  STATIC PROTOTYPES
 **********************/
static VOID bt_handle_common(struct bt_task_event * event);
static VOID bt_handle_hfp(struct bt_task_event * event);
static VOID bt_handle_acl(struct bt_task_event * event);
static VOID bt_handle_a2dp(struct bt_task_event * event);
static VOID bt_adp_main(VOID * para);
static VOID bt_event_handle(struct bt_task_event * event);
static VOID bt_handle_avrcp(struct bt_task_event * event);
static VOID bt_save_paired_list_to_nv(VOID);
static VOID bt_load_paired_list_from_nv(VOID);
static VOID bt_update_connect_status(bt_paired_dev_t * item);
static VOID bt_send_connect_message_to_ui(BOOL connect, BOOL for_audio);
static VOID bt_modify_paired_list(struct bt_addr addr, INT8 profile, INT8 connect);
static VOID bt_handle_hfp_ui(struct bt_task_event * event);
static VOID bt_handle_obex_ui(struct bt_task_event * event);
static VOID bt_file_send_malloc(VOID);
static VOID bt_file_send_free(VOID);
#ifndef USE_LV_PHONE
static BOOL bt_recv_file_check_space(UINT32 file_size);
#endif

static const INT8 * bt_parse_aclreason(UINT32 reason);
static const INT8 * bt_parse_keyvalue(UINT32 reason);
#ifndef USE_LV_PHONE
static const INT8 * bt_parse_putfailedreason(UINT32 reason);
#endif

extern VOID bt_usr_handle_common(struct bt_task_event * msg);
extern VOID bt_usr_handle_hfp(struct bt_task_event * msg);
extern VOID bt_usr_handle_sco(struct bt_task_event * msg);
extern VOID bt_usr_handle_acl(struct bt_task_event * msg);
extern VOID bt_usr_handle_a2dp(struct bt_task_event * msg);
extern VOID bt_mgr_handler(struct bt_task_event * msg);
extern VOID bt_usr_handle_avrcp(struct bt_task_event * msg);
#if USE_LV_BLE_DEMO != 0
extern VOID bt_usr_handle_le(struct bt_task_event * msg);
#endif

#if USE_LV_WATCH_DEMO != 0
extern BOOL bluetooth_setting_is_active(VOID);
extern VOID app_adp_get_imei_req(INT8 * imei);
extern VOID watch_disable_wakeup_lcd(VOID);
#endif
#if USE_LV_PHONE != 0
extern BOOL Bluetooth_Setting_Is_Top(VOID);
#endif

#if CONFIG_USE_BT_RLT8763BO == 1
extern int bbpro_sw_deinit(void);
extern int bbpro_hw_deinit(void);
extern int bbpro_open(bt_event_handle_t handle);
extern int bbpro_close(void);
extern int bbpro_inquiry(void);
extern int bbpro_inquiry_stop(void);
extern int bbpro_create_connection(uint8_t *bt_addr);
#endif

#if USE_LV_WLAN != 0
static void bt_handle_fw_assert_from_wlan(void);
#endif

/**********************
 *  STATIC VARIABLES
 **********************/
extern TASK_HANDLE * gui_task_id;

static TASK_HANDLE * bt_adp_task_id = NULL;
static bt_paired_dev_t * bt_paired_list = NULL;
static struct bt_event_inquiry * bt_scan_list = NULL;
static BOOL bt_headset_connect = false;
static BOOL bt_headset_hfp_connect = false;
static UINT8 bt_scan_dev_cnt = 0;
static UINT8 bt_paired_dev_cnt = 0;
static UINT8 bt_inquiry_cur_state = BT_INQUIRY_INIT;
static struct bt_addr acl_addr;
static UINT32 bt_current_tid = 0;
static bt_file_receive_context * bt_receive_context = NULL;
static bt_file_send_context * bt_send_context = NULL;
static struct bt_task_obex_file_send * bt_obex_chunk = NULL;
#ifndef USE_LV_PHONE
static BOOL bt_firstput = true;
#endif
static UINT16 acl_handle = 0;
static BOOL bt_fw_assert = false;
static BOOL bt_opening = false;
static bool bt_open_res = false;

/*wib pin to bt functiont*/
static const uint32 mfp_cfg_for_bt[] = {
  /*WIB function*/
  MFP_REG(GPIO_79_TDS_DIO11) | MFP_AF0 | MFP_DRIVE_MEDIUM | MFP_PULL_HIGH | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_80_TDS_DIO12) | MFP_AF0 | MFP_DRIVE_MEDIUM | MFP_PULL_LOW | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_VCXO_REQ) | MFP_AF0 | MFP_DRIVE_MEDIUM | MFP_PULL_LOW | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_86) | MFP_AF0 | MFP_DRIVE_MEDIUM | MFP_PULL_LOW | MFP_SLEEP_FLOAT | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_51) | MFP_AF1 | MFP_DRIVE_MEDIUM | MFP_PULL_HIGH | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_52) | MFP_AF1 | MFP_DRIVE_MEDIUM | MFP_PULL_HIGH | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_53) | MFP_AF1 | MFP_DRIVE_MEDIUM | MFP_PULL_HIGH | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_54) | MFP_AF1 | MFP_DRIVE_MEDIUM | MFP_PULL_HIGH | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_EOC
};

/*wib pin to gpio functiont*/
static const uint32 mfp_cfg_for_bt_gpio[] = {
  /*WIB function*/
  MFP_REG(GPIO_79_TDS_DIO11) | MFP_AF0 | MFP_DRIVE_MEDIUM | MFP_PULL_LOW | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_80_TDS_DIO12) | MFP_AF0 | MFP_DRIVE_MEDIUM | MFP_PULL_LOW | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_VCXO_REQ) | MFP_AF1 | MFP_DRIVE_MEDIUM | MFP_PULL_LOW | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_86) | MFP_AF0 | MFP_DRIVE_MEDIUM | MFP_PULL_LOW | MFP_SLEEP_FLOAT | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_51) | MFP_AF0 | MFP_DRIVE_MEDIUM | MFP_PULL_HIGH | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_52) | MFP_AF0 | MFP_DRIVE_MEDIUM | MFP_PULL_HIGH | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_53) | MFP_AF0 | MFP_DRIVE_MEDIUM | MFP_PULL_HIGH | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_REG(GPIO_54) | MFP_AF0 | MFP_DRIVE_MEDIUM | MFP_PULL_HIGH | MFP_SLEEP_NONE | MFP_LPM_EDGE_NONE,
  MFP_EOC
};

#if USE_LV_WATCH_SD_AUTO_MOUNT != 0
extern bool setting_sd_mount_capture_check(uint8_t app_type);
extern void setting_sd_mount_app_release(uint8_t app_type);
#define CAPTURE_SD_APP_BLUETOOTH     0x01
#endif
/**********************
 *      MACROS
 **********************/
#define BT_ADP_TASK_PRIORITY 199

/**********************
 *   GLOBAL FUNCTIONS
***********************/
void bt_pin_config(uint8_t on)
{
    if (on)
        mfp_config((uint32_t *)mfp_cfg_for_bt);
    else
        mfp_config((uint32_t *)mfp_cfg_for_bt_gpio);

    return;
}

/*
*  add device from scanlist to PairedList
*/
INT32 bt_add_device(struct bt_addr addr, UINT32 cod)
{
    INT32 ret = 0;

    printf("bt_add_device()++,  addr: %02x%02x%02x%02x%02x%02x, cod: 0x%08x\n",
           addr.bytes[0], addr.bytes[1], addr.bytes[2],
           addr.bytes[3], addr.bytes[4], addr.bytes[5],
           cod);

    if((cod & COD_TYPE_PHONE) == COD_TYPE_PHONE) {
        ret = appbt_bonding(addr);
    } else if((cod & COD_TYPE_HEADSET) == COD_TYPE_HEADSET) {
        ret = appbt_connect_headset(addr, 0);
    } else {
        printf("unknown COD: 0x08%x\n", cod);
    }
    printf("bt_add_device()--, ret: %d\n", ret);
    return ret;
}

/*
*  connect device in the PairedList
*/
INT32 bt_connect_device(struct bt_addr addr, UINT32 cod)
{
    INT32 ret = 0;

    printf("bt_connect_device()++,  addr: %02x%02x%02x%02x%02x%02x, cod: 0x%08x\n",
           addr.bytes[0], addr.bytes[1], addr.bytes[2],
           addr.bytes[3], addr.bytes[4], addr.bytes[5],
           cod);

#if CONFIG_USE_BT_RLT8763BO == 1
    ret =  bbpro_create_connection((uint8_t *)&addr);
#else
    INT32 call_status = 0;
    if(acl_handle != 0 && (memcmp(addr.bytes, acl_addr.bytes, 6) == 0)) {
        printf("bt_connect_device()--, already connecting.....");
        return 0;
    }
    if((cod & COD_TYPE_PHONE) == COD_TYPE_PHONE) {
        ret = appbt_bonding(addr);
    } else if((cod & COD_TYPE_HEADSET) == COD_TYPE_HEADSET) {
        ret = appbt_connect_headset(addr, call_status);
    } else {
        printf("unknown COD: 0x08%x\n", cod);
    }
#endif
    printf("bt_connect_device()--, ret: %d\n", ret);
    return ret;
}

/*
* disconnect device in PairedList only
*/
INT32 bt_disconnect_device(struct bt_addr addr, UINT32 cod)
{
    INT32 ret = 0;

    printf("bt_disconnect_device()++, addr: %02x%02x%02x%02x%02x%02x, cod: 0x%08x\n",
           addr.bytes[0], addr.bytes[1], addr.bytes[2],
           addr.bytes[3], addr.bytes[4], addr.bytes[5],
           cod);

#if CONFIG_USE_BT_RLT8763BO == 1
    bt_modify_paired_list(addr, PROFILE_CONNECTION, PROFILE_STATE_DISCONNECTED);
#else
    if((cod & COD_TYPE_PHONE) == COD_TYPE_PHONE) {
        ret = appbt_obex_disconnect(bt_current_tid);
    } else if((cod & COD_TYPE_HEADSET) == COD_TYPE_HEADSET) {
        if(acl_handle == 0) return -1;
        ret = appbt_disconnect_headset(NULL);
        if(ret == 0) {
            UINT8 i = 0;
            for(i = 0; i < bt_paired_dev_cnt; i++) {
                if(memcmp(addr.bytes, bt_paired_list[i].addr, 6) == 0) {
                    bt_paired_list[i].auto_connect = 0;
                    printf("bt disconnect by user, auto_connect = 0!!\n");
                }
            }
        }
    } else {
        printf("UNKNOWN COD: 0x08%x\n", cod);
    }
#endif
    printf("bt_disconnect_device()--, ret: %d\n", ret);
    return ret;
}

INT32 bt_accept_connect_device(struct bt_addr addr, UINT32 accept)
{
    INT32 ret = 0;

    printf("bt_accept_connect_device()++, addr: %02x%02x%02x%02x%02x%02x, accept: %d\n",
           addr.bytes[0], addr.bytes[1], addr.bytes[2],
           addr.bytes[3], addr.bytes[4], addr.bytes[5],
           accept);
    if(accept) ret = appbt_hfp_accept_connection(1, addr);
    else ret = appbt_hfp_reject_connection(addr);
    printf("bt_accept_connect_device()--, ret: %d\n", ret);
    return ret;
}

/*
* disconnect and remove pairing info
*/
INT32 bt_remove_device(struct bt_addr addr, UINT32 cod)
{
    INT32 ret = 0;

    printf("bt_remove_device()++, addr: %02x%02x%02x%02x%02x%02x, cod: 0x%08x\n",
           addr.bytes[0], addr.bytes[1], addr.bytes[2],
           addr.bytes[3], addr.bytes[4], addr.bytes[5],
           cod);

#if CONFIG_USE_BT_RLT8763BO == 1
    bt_modify_paired_list(addr, PROFILE_CONNECTION, PROFILE_STATE_DISCONNECTED);
    bt_modify_paired_list(addr, PROFILE_PAIRED, PROFILE_STATE_DISCONNECTED);
#else
    if((cod & COD_TYPE_PHONE) == COD_TYPE_PHONE) {
        ret = appbt_unbond(addr);
        if(ret == 0) {
            bt_modify_paired_list(addr, PROFILE_PAIRED, PROFILE_STATE_DISCONNECTED);
        }
    } else if((cod & COD_TYPE_HEADSET) == COD_TYPE_HEADSET) {
        bt_modify_paired_list(addr, PROFILE_PAIRED, PROFILE_STATE_DISCONNECTED);
        ret = appbt_disconnect_headset(&addr);
    } else {
        printf("UNKNOWN COD: 0x08%x\n", cod);
    }
#endif
    printf("bt_remove_device()--, ret: %d\n", ret);
    return ret;
}

/*
* scan available device
*/
INT32 bt_inquiry_device(VOID)
{
    INT32 ret = 0;
#if CONFIG_USE_BT_RLT8763BO == 1
    ret = bbpro_inquiry();
#else
    UINT8 inquiry_length = 0x15;
    ret = appbt_inquiry(inquiry_length, BT_SCAN_NUM);
#endif
    printf("bt_inquiry_device()--, ret: %d\n", ret);
    bt_inquiry_cur_state = BT_INQUIRY_INQUIRYING;
    printf("%s:bt_inquiry_cur_state = %d\n", __FUNCTION__, bt_inquiry_cur_state);
    return ret;
}

/*
* cancel scan available device
*/
INT32 bt_inquiry_device_cancel(VOID)
{
    INT32 ret = 0;
#if CONFIG_USE_BT_RLT8763BO == 1
    ret =  bbpro_inquiry_stop();
#else
    if(bt_inquiry_cur_state == BT_INQUIRY_INQUIRYING) {
        if(bt_fw_assert) {
            mmi_msg_bt_inquiry_response_t * msg = (mmi_msg_bt_inquiry_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_inquiry_response_t));
            msg->header.MsgId = MMI_BT_INQUIRY_CNF;
            msg->complete = 1;
            Hal_Send_Message((TASK_ID)gui_task_id, (VOID *)msg);
            bt_inquiry_cur_state = BT_INQUIRY_COMPLETE;
            printf("bt_inquiry_device_cancel()--, stop inquiry if bt fw assert\n");
            return 0;
        } else {
            ret = appbt_inquiry_cancel();
            bt_inquiry_cur_state = BT_INQUIRY_CANCELING;
            printf("bt_inquiry_device_cancel()--, ret: %d\n", ret);
        }
    }
#endif
    printf("%s:bt_inquiry_cur_state = %d\n", __FUNCTION__, bt_inquiry_cur_state);
    return ret;
}

/*
* pairing device from scan list
*/
INT32 bt_bonding_device(struct bt_addr addr, UINT32 cod)
{
    INT32 ret = 0;
    printf("bt_bonding_device()++, addr: %02x%02x%02x%02x%02x%02x, cod: %x\n",
           addr.bytes[0], addr.bytes[1], addr.bytes[2],
           addr.bytes[3], addr.bytes[4], addr.bytes[5],
           cod);
#if CONFIG_USE_BT_RLT8763BO == 1
    ret = bbpro_create_connection((uint8_t *)&addr);
#else
    if((cod & COD_TYPE_PHONE) == COD_TYPE_PHONE) {
        printf("bt bounding COD: phone\n");
        ret = appbt_bonding(addr);
    } else if((cod & COD_TYPE_HEADSET) == COD_TYPE_HEADSET) {
        printf(" bt bounding COD: headset\n");
        ret = appbt_connect_headset(addr, 0);
    } else {
        printf("UNKNOWN COD: 0x08%x\n", cod);
        ret = -1;
    }
#endif
    printf("bt_bonding_device()--, ret: %d\n", ret);
    return ret;
}

/*
* pairing device accept
*/
INT32 bt_accept_bonding_device(struct bt_addr addr, UINT32 accept)
{
    int8_t ret = 0;
    printf("bt_accept_bonding_device()++, accept:%d,addr: %02x%02x%02x%02x%02x%02x\n",
           accept, addr.bytes[0], addr.bytes[1], addr.bytes[2],
           addr.bytes[3], addr.bytes[4], addr.bytes[5]);
    ret = appbt_accept_bonding(addr, accept);
    printf("bt_accept_bonding_devicee()--, ret: %d\n", ret);
    return ret;
}

/*
* unpairing device
*/
INT32 bt_unbond_device(struct bt_addr addr)
{
    INT32 ret = 0;
    ret = appbt_unbond(addr);
    printf("bt_unbond_device()--, ret: %d\n", ret);
    return ret;
}

/*
* open bt device
*/
INT32 bt_open_device(VOID)
{
    INT32 ret = 0;
    printf("bt_open++ bt_opening=%d\n", bt_opening);

#if USE_LV_BLE_DEMO
    appbt_config_bt_mode(BT_MODE_BR_LE);
#endif

#if CONFIG_USE_BT_RLT8763BO == 1
    ret = bbpro_open(bt_event_handle);
#else
    if(bt_opening) return ret;
    bt_opening = true;
    struct bt_user_init_cfg pcfg;
    memset(&pcfg, 0, sizeof(struct bt_user_init_cfg));
    INT8 m_text[9];
    bt_get_device_name(m_text);
    UINT8 i = 0;

    for(i = 0; i < strlen(m_text); i++) {
        if(i == MAX_BT_NAME - 1) {
            pcfg.name[i] = '\0';
            break;
        }
        pcfg.name[i] = m_text[i];
    }
    pcfg.name[i] = '\0';
    pcfg.discover_connect_able = 1;
    pcfg.bt_wakeup_host_pin = 60;
#if 0
    pcfg.bt_firmware = (UINT8 *)build_ram_only;
    pcfg.bt_firmware_size = build_ram_only_len;
    pcfg.bt_nvm_lst = (UINT8 *)bt_update;
    pcfg.bt_nvm_lst_size = bt_update_len;
    pcfg.firmware_where = BT_FIRMWARE_AT_MEMORY;
#endif
    firmware_load_info_t load_info;
    ret = firmware_get_load_info(FIRMWARE_WCN_BT_BTBIN, &load_info);
    if((ret != 0) || (load_info.size == 0)) {
        printf("%s: no btbin bin, pls check, ret %d\n", __FUNCTION__, ret);
        return ret;
    }
    _ptentry * ptn = ptable_find_entry(load_info.partition_name);
    if(!ptn) {
        printf("%s: no ptn for btbin with pname %s\n", __FUNCTION__, load_info.partition_name);
        return -1;
    }
    pcfg.bt_firmware = (UINT8 *)(ptn->vstart + load_info.offset);
    pcfg.bt_firmware_size = load_info.size;
    ret = firmware_get_load_info(FIRMWARE_WCN_BT_BTLST, &load_info);
    if((ret != 0) || (load_info.size == 0)) {
        printf("%s: no btlst bin, pls check, ret %d\n", __FUNCTION__, ret);
        return ret;
    }
    ptn = ptable_find_entry(load_info.partition_name);
    if(!ptn) {
        printf("%s: no ptn for btlst with pname %s\n", __FUNCTION__, load_info.partition_name);
        return -1;
    }
    pcfg.bt_nvm_lst = (UINT8 *)(ptn->vstart + load_info.offset);
    pcfg.bt_nvm_lst_size = load_info.size;
    pcfg.firmware_where = BT_FIRMWARE_AT_FLASH;

    printf("firmware: 0x%x, size: %d, lst: 0x%x, size: %d\n",
           pcfg.bt_firmware, pcfg.bt_firmware_size, pcfg.bt_nvm_lst, pcfg.bt_nvm_lst_size);
    printf("firmware_where: %d\n", pcfg.firmware_where);

    ret = appbt_open(bt_event_handle, &pcfg);
#endif
    printf("bt_open_device()--, ret: %d\n", ret);
    return ret;
}

/*
* close bt device
*/
INT32 bt_close_device(VOID)
{
    INT32 ret = 0;

#if USE_LV_BLE_DEMO
    ble_deinit();
#endif

#if CONFIG_USE_BT_RLT8763BO == 1
    bbpro_close();
    bbpro_sw_deinit();
    bbpro_hw_deinit();
#else
    ret = appbt_close();
#endif
    printf("bt_close_device()--, ret: %d\n", ret);
    return ret;
}

/*
* change device local name
*/
INT32 bt_change_local_devname(const INT8 * name)
{
    INT32 ret = 0;
    ret = appbt_change_local_name(name);
    printf("bt_change_local_name()--,ret: %d\n", ret);
    return ret;
}

BOOL bt_get_state(VOID)
{
    printf("bt_get_state %d\n", appbt_get_open_status());
    return appbt_get_open_status();
}

VOID bt_adp_task_create(VOID)
{
    if(NULL == bt_adp_task_id)
#if UI_IN_XIP
    {
        static lv_xip_thread param0;
        param0.func = bt_adp_main;
        param0.params = NULL;
        bt_adp_task_id = uos_create_task(
            lv_xip_thread_entry,
            &param0,
            64,
            4*1000,
            BT_ADP_TASK_PRIORITY,
            "bt_adpator");
    }
#else
        bt_adp_task_id = uos_create_task(bt_adp_main, NULL, 64, 4 * 1000, BT_ADP_TASK_PRIORITY, "bt_adpator");
#endif
}

VOID bt_adp_task_destroy(VOID)
{
    if(NULL != bt_adp_task_id) {
        uos_delete_task(bt_adp_task_id);
        bt_adp_task_id = NULL;
    }
}

VOID bt_list_init(VOID)
{
    if(bt_paired_list == NULL) {
        bt_paired_list = (bt_paired_dev_t *)lv_mem_alloc(BT_PAIRED_NUM * sizeof(bt_paired_dev_t));
        if(!bt_paired_list) {
            printf("bt_paired_list init failed\n");
            return;
        }
        memset(bt_paired_list, 0, BT_PAIRED_NUM * sizeof(bt_paired_dev_t));
        bt_load_paired_list_from_nv();
    }
    if(bt_scan_list == NULL) {
        bt_scan_list = (struct bt_event_inquiry *)lv_mem_alloc(BT_SCAN_NUM * sizeof(struct bt_event_inquiry));
        if(!bt_scan_list) {
            printf("bt_scan_list init failed\n");
            return;
        }
        memset(bt_scan_list, 0, BT_SCAN_NUM * sizeof(struct bt_event_inquiry));
    }
}

VOID bt_list_destroy(VOID)
{
    bt_save_paired_list_to_nv();
    if(bt_scan_list) {
        lv_mem_free(bt_scan_list);
        bt_scan_list = NULL;
    }
    bt_scan_dev_cnt = 0;
    if(bt_paired_list) {
        lv_mem_free(bt_paired_list);
        bt_paired_list = NULL;
    }
    bt_paired_dev_cnt = 0;
}

BOOL bt_get_hfp_connect(VOID)
{
    printf("bt hfp connect %d\n", bt_headset_hfp_connect);
    return bt_headset_hfp_connect;
}

BOOL bt_get_headset_connect(VOID)
{
    printf("bt headset connect %d\n", bt_headset_connect);
    return bt_headset_connect;
}

UINT32 bt_get_scan_dev_cnt(VOID)
{
    return bt_scan_dev_cnt;
}

UINT32 bt_get_paired_dev_cnt(VOID)
{
    return bt_paired_dev_cnt;
}

UINT8 bt_get_inquiry_state(VOID)
{
    return bt_inquiry_cur_state;
}

struct bt_event_inquiry * bt_get_scan_list(VOID)
{
    return bt_scan_list;
}

bt_paired_dev_t * bt_get_paired_list(VOID)
{
    return bt_paired_list;
}

struct bt_event_inquiry * bt_get_scan_dev(UINT8 index)
{
    struct bt_event_inquiry * inquiry;
    if(index >= bt_scan_dev_cnt)
        return NULL;
    inquiry = &bt_scan_list[index];
    return inquiry;
}

VOID bt_clean_scan_list(VOID)
{
    if(bt_scan_list) {
        memset(bt_scan_list, 0, BT_SCAN_NUM * sizeof(struct bt_event_inquiry));
        bt_scan_dev_cnt = 0;
    }
}

bt_file_send_context * bt_get_send_file_info(VOID)
{
    return bt_send_context;
}

bt_file_receive_context * bt_get_receive_file_info(VOID)
{
    return bt_receive_context;
}

VOID bt_file_receive_cancel(VOID)
{
    UINT32 ret = appbt_obex_send_response(bt_current_tid, OBEX_RESPONSE_NOT_ACCEPTABLE);
    printf("bt_file_receive_cancel()--,  bt_current_tid = %d ret: %d\n", bt_current_tid, ret);
}

VOID bt_file_receive_continue(VOID)
{
    UINT32 ret = appbt_obex_send_response(bt_current_tid, OBEX_RESPONSE_CONTINUE);
    printf("bt_file_receive_continue()--, bt_current_tid = %d ret: %d\n", bt_current_tid, ret);
}

VOID bt_file_receive_ok(VOID)
{
    UINT32 ret = appbt_obex_send_response(bt_current_tid, OBEX_RESPONSE_OK);
    printf("bt_file_receive_ok()--, bt_current_tid = %d ret: %d\n", bt_current_tid, ret);
}

VOID bt_clear_received_file(VOID)
{
    if(bt_receive_context) {
        bt_receive_context->disconnect_reason = BT_RECEIVE_MAX;
    }
}

VOID bt_get_device_name(INT8 * name)
{
    if(NULL == name) return;
    #if USE_LV_WATCH_DEMO != 0
    INT8 imei[16];
    INT8 temp[9];
    app_adp_get_imei_req(imei);
    snprintf(temp, 9, "%s_%d%d", "Watch", imei[14], imei[15]);
    memcpy(name, temp, 9);
    #endif
}

VOID bt_power_off(VOID)
{
    if(bt_get_state()) {
        bt_inquiry_device_cancel();
        bt_close_device();
    }
}

VOID bt_send_file_pre(INT8 * file_name, struct bt_addr addr)
{
    INT32 file_name_len;
    INT32 ret = 0;

    printf("bt_send_file_pre()++\n");

    if(file_name == NULL) {
        printf("bt_send_file_pre empty file name\n");
        return;
    }
    if(bt_fw_assert || acl_handle != 0) {
        printf("bt_send_file_pre bt fw assert: %d or acl connectted:%d !! try again later\n", bt_fw_assert, acl_handle);
        mmi_msg_bt_obex_t * msg = (mmi_msg_bt_obex_t *)lv_mem_alloc(sizeof(mmi_msg_bt_obex_t));
        msg->header.MsgId = MMI_BT_OBEX_FILE_SEND_FAILED;
        Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
        return;
    }

    if(bt_send_context == NULL) {
        bt_file_send_malloc();
        memcpy(bt_send_context->addr.bytes, addr.bytes, 6);
        file_name_len = strlen(file_name) < 255 - 1 ? strlen(file_name) : 255 - 1;
        memcpy(bt_send_context->file_name, file_name, file_name_len);
        bt_send_context->file_name[file_name_len] = '\0';
        printf("bt_send_file_pre appbt_connect_phone()++, addr: %02x%02x%02x%02x%02x%02x\n",
               addr.bytes[0], addr.bytes[1], addr.bytes[2],
               addr.bytes[3], addr.bytes[4], addr.bytes[5]);
        ret = appbt_connect_phone(bt_send_context->addr);
        printf("appbt_connect_phone()--, ret: %d\n", ret);
        if(ret != 0) {
            bt_file_send_free();
            mmi_msg_bt_obex_t * msg = (mmi_msg_bt_obex_t *)lv_mem_alloc(sizeof(mmi_msg_bt_obex_t));
            msg->header.MsgId = MMI_BT_OBEX_FILE_SEND_FAILED;
            Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
        }
    } else {
        printf("bt_send_file_pre()+++ A file is sending...\n");
    }
    printf("bt_send_file_pre()--\n");

}

INT32 bt_file_send_cancel(VOID)
{
    printf("bt_file_send_cancel()++, acl_handle: %d\n", acl_handle);
    INT32 ret = appbt_acl_disconnect(acl_handle);
    printf("bt_file_send_cancel()--, ret: %d\n", ret);
    return ret;
}

/**********************
 *   STATIC FUNCTIONS
 **********************/
static VOID bt_save_paired_list_to_nv(VOID)
{
    UINT8 i = 0;
    #if USE_LV_WATCH_DEMO != 0
    UINT32 length = sizeof(nv_watch_bluetooth_t);
    nv_watch_bluetooth_t * nvm = (nv_watch_bluetooth_t *)lv_mem_alloc(length);
    #endif
    #if USE_LV_PHONE != 0
    UINT32 length = sizeof(NV_UI_Bluetooth_t);
    NV_UI_Bluetooth_t * nvm = (NV_UI_Bluetooth_t *)lv_mem_alloc(length);
    #endif
    if(length != UI_NV_Read_Req(NV_SECTION_UI_BLUETOOTH, 0, length, (UINT8 *)nvm)) {
        printf("read bluetooth from nvm error\n");
        lv_mem_free(nvm);
        return;
    }
    UINT8 on_off = nvm->on_off;
    memset(nvm, 0, length);
    uint8_t start = 0;
    if(bt_paired_dev_cnt > NV_BLUETOOTH_MAX_DEV_CNT)
        start = bt_paired_dev_cnt - NV_BLUETOOTH_MAX_DEV_CNT;
    for(i = start; i < bt_paired_dev_cnt; i++) {
        if(bt_paired_list[i].profile_status[PROFILE_PAIRED] == PROFILE_STATE_CONNECTED) {
            memcpy(nvm->dev_name[i - start], bt_paired_list[i].name, MAX_BT_NAME);
            memcpy(nvm->addr[i- start], bt_paired_list[i].addr, 6);
            nvm->cod[i - start] = bt_paired_list[i].cod;
            nvm->length[i - start] = bt_paired_list[i].length;
            nvm->dev_cnt++;
        }
    }
    nvm->on_off = on_off;
    UI_NV_Write_Req(NV_SECTION_UI_BLUETOOTH, 0, length, (UINT8 *)nvm);
    lv_mem_free(nvm);
}

static VOID bt_load_paired_list_from_nv(VOID)
{
    UINT8 i = 0;
    #if USE_LV_WATCH_DEMO != 0
    UINT32 length = sizeof(nv_watch_bluetooth_t);
    nv_watch_bluetooth_t * nvm = (nv_watch_bluetooth_t *)lv_mem_alloc(length);
    #endif
    #if USE_LV_PHONE != 0
    UINT32 length = sizeof(NV_UI_Bluetooth_t);
    NV_UI_Bluetooth_t * nvm = (NV_UI_Bluetooth_t *)lv_mem_alloc(length);
    #endif
    if(length != UI_NV_Read_Req(NV_SECTION_UI_BLUETOOTH, 0, length, (UINT8 *)nvm)) {
        printf("read bluetooth from nvm error\n");
        lv_mem_free(nvm);
        return;
    }
    for(i = 0; i < nvm->dev_cnt; i++) {
        memcpy(bt_paired_list[i].name, nvm->dev_name[i], MAX_BT_NAME);
        memcpy(bt_paired_list[i].addr, nvm->addr[i], 6);
        bt_paired_list[i].length = nvm->length[i];
        bt_paired_list[i].cod = nvm->cod[i];
        bt_paired_list[i].profile_status[PROFILE_PAIRED] = PROFILE_STATE_CONNECTED;
        if((bt_paired_list[i].cod & COD_TYPE_HEADSET) == COD_TYPE_HEADSET) {
            bt_paired_list[i].profile_status[PROFILE_ACL] = PROFILE_STATE_DISCONNECTED;
            bt_paired_list[i].profile_status[PROFILE_CONNECTION] = PROFILE_STATE_DISCONNECTED;
            bt_paired_list[i].auto_connect = 1;
        }
        printf("bt_load_paired_list_from_nv: addr %02x%02x%02x%02x%02x%02x,cod 0x%08x. \n",
               bt_paired_list[i].addr[0], bt_paired_list[i].addr[1], bt_paired_list[i].addr[2],
               bt_paired_list[i].addr[3], bt_paired_list[i].addr[4], bt_paired_list[i].addr[5], bt_paired_list[i].cod);
        printf("bt_load_paired_list_from_nv: name %s auto=%d\n", bt_paired_list[i].name, bt_paired_list[i].auto_connect);
    }
    bt_paired_dev_cnt = nvm->dev_cnt;
    printf("bt_load_paired_list_from_nv devcnt = %d\n", bt_paired_dev_cnt);
    lv_mem_free(nvm);
}

static void bt_check_invalid_char(char * name, uint8_t len)
{
    UINT8 i = 0;
    bool inv_char = false;
    printf("bt_check_invalid_char len=%d\n",len);
    for(i = 0; i < len; i++) {
        if(inv_char && i + 2 < MAX_BT_NAME) {
            name[i] = name[i + 2];
        }
        if(!inv_char && name[i] == 0xe2) {
            name[i] = 0x20;
            inv_char = true;
            printf("bt_check_invalid_char invalid i=%d\n", i);
        }
    }
}

static VOID bt_add_scan_list(struct bt_event_inquiry * inquiry)
{
    printf("bt_add_scan_list: addr %02x%02x%02x%02x%02x%02x,cod 0x%08x. \n",
           inquiry->addr[0], inquiry->addr[1], inquiry->addr[2],
           inquiry->addr[3], inquiry->addr[4], inquiry->addr[5], inquiry->cod);
    printf("bt_add_scan_list: name %s\n", inquiry->name);
    if(bt_scan_dev_cnt > BT_SCAN_NUM - 1) {
        printf("bt_add_scan_list: find too much device\n");
        return;
    }

    UINT8 i = 0;
    for(i = 0; i < bt_scan_dev_cnt; i++) {
        if(memcmp(inquiry->addr, bt_scan_list[i].addr, 6) == 0) {
            printf("bt bluetooth_add_scan_list: find the same device\n");
            return;
        }
    }
    bt_check_invalid_char(inquiry->name, inquiry->length);
    memcpy(&bt_scan_list[bt_scan_dev_cnt], inquiry, sizeof(struct bt_event_inquiry));
    bt_scan_dev_cnt++;
}

static void bt_remove_from_scan_list(struct bt_addr addr)
{
    UINT8 i = 0;
    BOOL b_find = false;
    for(i = 0; i < bt_scan_dev_cnt; i++) {
        if(memcmp(addr.bytes, bt_scan_list[i].addr, 6) == 0 && !b_find) {
            printf("bt bluetooth_remove_scan_list: find the same device\n");
            memset(&bt_scan_list[i], 0, sizeof(struct bt_event_inquiry));
            b_find = true;
        }
        if(b_find) {
            memcpy(&bt_scan_list[i], &bt_scan_list[i + 1], sizeof(struct bt_event_inquiry));
        }
    }
    if(b_find) bt_scan_dev_cnt--;
    printf("bt bluetooth_remove_scan_list: scan dev %d\n", bt_scan_dev_cnt);
}

static VOID bt_disconnect_headset_fw_assert(VOID)
{
    if(bt_get_headset_connect()) {
        UINT8 i = 0;
        struct bt_addr addr;
        for(i = 0; i < bt_get_paired_dev_cnt(); i++)
        {
            if((bt_paired_list[i].cod & COD_TYPE_HEADSET) == COD_TYPE_HEADSET
               && bt_paired_list[i].profile_status[PROFILE_CONNECTION] == PROFILE_STATE_CONNECTED) {
                printf("%s\n", __FUNCTION__);
                memcpy(addr.bytes, bt_paired_list[i].addr, 6);
                bt_modify_paired_list(addr, PROFILE_AVRCP, PROFILE_STATE_DISCONNECTED);
                bt_modify_paired_list(addr, PROFILE_HFP, PROFILE_STATE_DISCONNECTED);
                bt_modify_paired_list(addr, PROFILE_A2DP, PROFILE_STATE_DISCONNECTED);
                bt_modify_paired_list(addr, PROFILE_ACL, PROFILE_STATE_DISCONNECTED);
                bt_headset_hfp_connect = false;
                void * msg = NULL;
                msg = (mmi_msg_bt_hfp_common_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_hfp_common_response_t));
                ((mmi_msg_bt_hfp_common_response_t *)msg)->header.MsgId = MMI_BT_HFP_DISCONNECTED;
                Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
                break;
            }
        }
    }
}

static VOID bt_remove_from_paired_list(struct bt_addr addr)
{
    UINT8 i = 0;
    BOOL b_find = false;
    for(i = 0; i < bt_paired_dev_cnt; i++) {
        if(memcmp(addr.bytes, bt_paired_list[i].addr, 6) == 0 && !b_find) {
            printf("bt_remove_from_paired_list: find the same device\n");
            memset(&bt_paired_list[i], 0, sizeof(bt_paired_dev_t));
            b_find = true;
        }
        if(b_find) {
            memcpy(&bt_paired_list[i], &bt_paired_list[i + 1], sizeof(bt_paired_dev_t));
        }
    }
    if(b_find) bt_paired_dev_cnt--;
    printf("bt_remove_from_paired_list: paired dev %d\n", bt_paired_dev_cnt);
}

static VOID bt_restart_interrupted_inquiry(VOID)
{
    printf("bt_restart_interrupted_inquiry, bt_inquiry_cur_state = %d\n", bt_inquiry_cur_state);
    if(bt_inquiry_cur_state == BT_INQUIRY_INTERRUPT)
        bt_inquiry_device();
}

static VOID bt_modify_paired_list(struct bt_addr addr, INT8 profile, INT8 connect)
{
    BOOL b_find_pairlist = false;
    BOOL b_find_scanlist = false;
    BOOL b_find_record = false;
    UINT8 i = 0;
    UINT8 temp = bt_paired_dev_cnt;
    printf("bt_modify_paired_list: addr %02x%02x%02x%02x%02x%02x\n",
           addr.bytes[0], addr.bytes[1], addr.bytes[2],
           addr.bytes[3], addr.bytes[4], addr.bytes[5]);

    if(!bt_paired_list) {
        printf("bt_paired_list is not initialized, return NULL\n");
        return;
    }

    for(i = 0; i < bt_paired_dev_cnt; i++) {
        if(profile == PROFILE_ACL && connect == PROFILE_STATE_FAILED) {
            if(bt_paired_list[i].profile_status[PROFILE_CONNECTION] == PROFILE_STATE_CONNECTING) {
                printf("bt_modify_paired_list: reset auto-connect state if ACL doesn't connect success!!");
                bt_paired_list[i].profile_status[PROFILE_CONNECTION] = PROFILE_STATE_DISCONNECTED;
            }
        }
        if(memcmp(bt_paired_list[i].addr, addr.bytes, 6) == 0) {
            b_find_pairlist = true;
            bt_paired_list[i].profile_status[profile] = connect;
            temp = i;
            break;
        }
    }

    if((!b_find_pairlist) && (connect == PROFILE_STATE_CONNECTED)) {
        printf("bt_modify_paired_list: not find dev in bt_paired_list\n");
        memcpy(bt_paired_list[temp].addr, addr.bytes, 6);
        bt_paired_list[temp].profile_status[profile] = connect;
        bt_paired_dev_cnt++;
        for(i = 0; i < bt_scan_dev_cnt; i++) {
            if(memcmp(bt_paired_list[temp].addr, bt_scan_list[i].addr, 6) == 0) {
                memcpy(bt_paired_list[temp].name, bt_scan_list[i].name, MAX_BT_NAME);
                bt_paired_list[temp].length = bt_scan_list[i].length;
                bt_paired_list[temp].cod = bt_scan_list[i].cod;
                b_find_scanlist = true;
                printf("bt_modify_paired_list: find in bt_scan_list\n");
                break;
            }
        }

        if(!b_find_scanlist) {
            printf("bt_modify_paired_list: can not find dev in bt_scan_list\n");
            struct bt_device_record * record = bt_find_device_record(addr);
            if(record == NULL) {
                printf("bt_modify_paired_list: can not find dev in Record\n");
            } else {
                b_find_record = true;
                bt_check_invalid_char(record->name, strlen((const char*)record->name));
                memcpy(bt_paired_list[temp].name, record->name, MAX_BT_NAME);
                bt_paired_list[temp].length = MAX_BT_NAME;
                bt_paired_list[temp].cod = record->cod;
                if(profile == PROFILE_PAIRED && connect == PROFILE_STATE_CONNECTED)
                    bt_paired_list[temp].profile_status[PROFILE_ACL] = PROFILE_STATE_CONNECTED;
            }
        }

        if(!b_find_pairlist && !b_find_scanlist && !b_find_record) {
            memset(&bt_paired_list[temp], 0, sizeof(bt_paired_dev_t));
            bt_paired_dev_cnt--;
            printf("bt++  can't find in any list, free this device!!\n");
        }
    }

    if(b_find_pairlist || bt_paired_dev_cnt > temp) {
        bt_update_connect_status(&bt_paired_list[temp]);
    } else {
        printf("bt bluetooth_modify_paired_list: can not find item\n");
    }
}

static VOID bt_update_connect_status(bt_paired_dev_t * item)
{
    printf("bt bluetooth_update_connect_status:addr: %02x%02x%02x%02x%02x%02x\n",
           item->addr[0], item->addr[1], item->addr[2],
           item->addr[3], item->addr[4], item->addr[5]);
    printf("bt paired:%d acl:%d a2dp:%d media:%d avrcp %d hfp:%d connection %d\n",
           item->profile_status[PROFILE_PAIRED], item->profile_status[PROFILE_ACL],
           item->profile_status[PROFILE_A2DP], item->profile_status[PROFILE_A2DP_MEDIA],
           item->profile_status[PROFILE_AVRCP], item->profile_status[PROFILE_HFP],
           item->profile_status[PROFILE_CONNECTION]);

    if((item->cod & COD_TYPE_HEADSET) == COD_TYPE_HEADSET) {
        if((item->profile_status[PROFILE_ACL] == PROFILE_STATE_CONNECTED) &&
                (item->profile_status[PROFILE_A2DP] == PROFILE_STATE_CONNECTED) &&
                (item->profile_status[PROFILE_A2DP_MEDIA] == PROFILE_STATE_CONNECTED) &&
                (item->profile_status[PROFILE_AVRCP] == PROFILE_STATE_CONNECTED)) {
            if(item->profile_status[PROFILE_CONNECTION] != PROFILE_STATE_CONNECTED) {
                item->profile_status[PROFILE_CONNECTION] = PROFILE_STATE_CONNECTED;
                if(item->profile_status[PROFILE_PAIRED] != PROFILE_STATE_CONNECTED)
                    item->profile_status[PROFILE_PAIRED] = PROFILE_STATE_CONNECTED;
                bt_headset_connect = true;
                printf("bt HEADSET_CONNECTED\n");
                bt_restart_interrupted_inquiry();
                bt_send_connect_message_to_ui(true, true);
                item->auto_connect = 1;
            }
        }

        if((item->profile_status[PROFILE_ACL] == PROFILE_STATE_DISCONNECTED) ||
                (item->profile_status[PROFILE_A2DP] == PROFILE_STATE_DISCONNECTED) ||
                (item->profile_status[PROFILE_A2DP_MEDIA] == PROFILE_STATE_DISCONNECTED) ||
                (item->profile_status[PROFILE_AVRCP] == PROFILE_STATE_DISCONNECTED)) {
            if(item->profile_status[PROFILE_CONNECTION] != PROFILE_STATE_DISCONNECTED) {
                item->profile_status[PROFILE_CONNECTION] = PROFILE_STATE_DISCONNECTED;
                bt_headset_connect = false;
                bt_headset_hfp_connect = false;
                printf("bt HEADSET_DISCONNECTED\n");
                bt_send_connect_message_to_ui(false, true);
            } else {
                if(item->profile_status[PROFILE_PAIRED] == PROFILE_STATE_DISCONNECTED) {
                    printf("bt HEADSET_UNPAIRED\n");
                    struct bt_addr addr;
                    memcpy(addr.bytes, item->addr, 6);
                    bt_remove_from_paired_list(addr);
                    bt_save_paired_list_to_nv();
                    bt_send_connect_message_to_ui(false, false);
                }
            }
        }
    } else if((item->cod & COD_TYPE_PHONE) == COD_TYPE_PHONE) {
        if(item->profile_status[PROFILE_PAIRED] == PROFILE_STATE_CONNECTED) {
            if(item->profile_status[PROFILE_CONNECTION] != PROFILE_STATE_CONNECTED) {
                printf("bt PHONE_PAIRED\n");
                item->profile_status[PROFILE_CONNECTION] = PROFILE_STATE_CONNECTED;
            }
        }
        if(item->profile_status[PROFILE_PAIRED] == PROFILE_STATE_DISCONNECTED) {
            if(item->profile_status[PROFILE_CONNECTION] != PROFILE_STATE_DISCONNECTED) {
                printf("bt PHONE_DISPAIRED\n");
                item->profile_status[PROFILE_CONNECTION] = PROFILE_STATE_DISCONNECTED;
                struct bt_addr addr;
                memcpy(addr.bytes, item->addr, 6);
                bt_remove_from_paired_list(addr);
                bt_save_paired_list_to_nv();
                bt_send_connect_message_to_ui(false, false);
            }
        }
    } else {
        printf("bt bluetooth_update_connect_status:COD_TYPE_UNKNOWN: 0x%08x\n", item->cod);
    }
}

#ifndef USE_LV_PHONE
static BOOL bt_recv_file_check_space(UINT32 file_size)
{
    UINT32 total = 0;
    UINT32 free = 0;
    lv_fs_dir_t dir;
    memset(&dir, 0, sizeof(lv_fs_dir_t));
#if USE_LV_WATCH_SD_AUTO_MOUNT != 0
    if(true == setting_sd_mount_capture_check(CAPTURE_SD_APP_BLUETOOTH))
#else
    if(-1 == HAL_sd_user_set(true))
#endif
    {
        printf("%s,SD card is mounted as U storage!\n",__FUNCTION__);
        bt_receive_context->disconnect_reason = BT_RECEIVE_SD_USING;
        return false;
    }
    lv_fs_res_t res = lv_fs_dir_open(&dir, FILE_ROOT_DIR);
    if(res == LV_FS_RES_OK) {
        lv_fs_free_space(SD_FS_LETTER, &total, &free);
        printf("bt_check_space total = %dKB\n, free = %dKB\n file_size = %dB\n", total, free, file_size);
        lv_fs_dir_close(&dir);
        if(free * 1000 <= file_size) {
            bt_receive_context->disconnect_reason = BT_RECEIVE_NO_SPACE;
            return false;
        }
        return true;
    } else {
        bt_receive_context->disconnect_reason = BT_RECEIVE_NO_SDCARD;
        printf("bt_check_space SD card is not exist!!!\n");
        return false;
    }
}

static INT8 * bt_recv_file_check_type(INT8 * filename, INT8 * file_type)
{
#if 1
    return FILE_DIR;
#else
    INT8 * filedir = NULL;
    INT8 expand_name[5];
    UINT8 i = 0;
    if(strcmp(file_type, "text/plain") == 0) {
        filedir = FILE_TEXT_DIR;
    } else if((strcmp(file_type, "image/jpeg") == 0)
              || (strcmp(file_type, "image/bmp") == 0)
              || (strcmp(file_type, "image/gif") == 0)) {
        filedir = FILE_IMAGE_DIR;
    } else if((strcmp(file_type, "audio/mpeg") == 0)
              || (strcmp(file_type, "audio/x-wav") == 0)
              || (strcmp(file_type, "audio/mid") == 0)) {
        filedir = FILE_MUSIC_DIR;
    } else if(strcmp(file_type, "video/x-msvideo") == 0) {
        filedir = FILE_VIDEOS_DIR;
    } else {
    }

    if(filedir == NULL) {
        memcpy(expand_name, lv_fs_get_ext(filename), 5);
        for(i = 0; expand_name[i]; i++)
            expand_name[i] = tolower(expand_name[i]);
        printf("expand name: %s\n", expand_name);

        // text
        if(strcmp(expand_name, "txt") == 0) {
            filedir = FILE_TEXT_DIR;
        }
        // picture
        else if((strcmp(expand_name, "jpg") == 0)
                || (strcmp(expand_name, "jpe") == 0)
                || (strcmp(expand_name, "jpeg") == 0)
                || (strcmp(expand_name, "bmp") == 0)
                || (strcmp(expand_name, "gif") == 0)) {
            filedir = FILE_IMAGE_DIR;
        }
        // audio
        else if((strcmp(expand_name, "mp3") == 0)
                || (strcmp(expand_name, "wav") == 0)
                || (strcmp(expand_name, "mid") == 0)
                || (strcmp(expand_name, "midi") == 0)) {
            filedir = FILE_MUSIC_DIR;
        }

        // video
        else if(strcmp(expand_name, "avi") == 0) {
            filedir = FILE_VIDEOS_DIR;
        } else {
            filedir = FILE_DIR;
        }
    }
    return filedir;
#endif
}

static VOID bt_file_receive_malloc(VOID)
{
    if(NULL == bt_receive_context) {
        bt_receive_context = (bt_file_receive_context *)lv_mem_alloc(sizeof(bt_file_receive_context));
        memset(bt_receive_context, 0, sizeof(bt_file_receive_context));
    }
    LV_ASSERT_MEM(bt_receive_context);
}

static VOID bt_file_receive_free(VOID)
{
    if(NULL != bt_receive_context) {
        lv_mem_free(bt_receive_context);
        bt_receive_context = NULL;
    }
}

static INT32 bt_save_1st_receive_data(UINT8 * data, UINT32 len)
{
    if(data == NULL || len == 0)
        return -1;

    bt_receive_context->packet_data = lv_mem_alloc(len);
    bt_receive_context->packet_data_len = len;
    memcpy(bt_receive_context->packet_data, data, len);

    return 0;
}

static INT32 bt_write_1st_receive_data(VOID)
{
    UINT32 written = 0;
    if(NULL == bt_receive_context) {
        return -1;
    }

    if(NULL == bt_receive_context->fd.file_d) {
        printf("invalid fd\n");
        return -2;
    }

    if(bt_receive_context->packet_data_len == 0) {
        return -3;
    }

    lv_fs_write(&(bt_receive_context->fd), (UINT8 *)(bt_receive_context->packet_data), bt_receive_context->packet_data_len, &written);
    bt_receive_context->file_offset = bt_receive_context->file_offset + bt_receive_context->packet_data_len;
    printf("bt_write_1st_receive_data (%d/%d)\n", bt_receive_context->file_offset, bt_receive_context->file_size);

    if(written != bt_receive_context->packet_data_len)
        printf("expected written %d bytes, written %d bytes actually\n", bt_receive_context->packet_data_len, written);

    bt_receive_context->b_start_receive_data = 1;
    return 0;
}

static VOID bt_free_1st_receive_data(VOID)
{
    if(bt_receive_context->packet_data)
        lv_mem_free(bt_receive_context->packet_data);
    bt_receive_context->packet_data = NULL;
    bt_receive_context->packet_data_len = 0;
    printf("bt_free_1st_receive_data()\n");
}

static INT32 bt_get_unicode_len(INT8 * str)
{
    INT32 i = -1;
    if(str == NULL)
        return i;

    for(i = 0;; i += 2) {
        if(0 == str[i] && 0 == str[i + 1]) {
            break;
        }
    }

    return i;
}

static VOID bt_trans_filename_to_utf8(INT8 * name, UINT32 name_size, INT8 * filename_only)
{
    UINT8 i = 0;
    UINT16 c = 0;
    UINT32 strlen_filename_only;
    UINT16 * p16_BE;
    UINT16 * p16_LE;
    UINT16 * p_src;
    INT8 file_name_LE[MAX_BT_OBEX_FILE_NAME];
    INT32 unicode_name_size =  bt_get_unicode_len(name);
    printf("unicode filename len: %d\n", unicode_name_size);
    if(name_size != unicode_name_size) {
        printf("Warning!! file name size not match, %d, %d, use %d\n", unicode_name_size, name_size, name_size);
        unicode_name_size = name_size;
    }
    if(unicode_name_size > MAX_BT_OBEX_FILE_NAME)
        printf("Warning!! unicode filename out of array bound, %d\n", unicode_name_size);

    printf("unicode filename(BE): \n");
    for(i = 0; i < unicode_name_size; i++)
        printf("%02x", name[i]);
    printf("\n");


    // convert to LE
    p16_BE = (UINT16 *)name;
    p16_LE = (UINT16 *)file_name_LE;
    for(i = 0; i < unicode_name_size / 2; i++) {
        c = p16_BE[i];
        p16_LE[i] = VAL_SWAP16(c);
    }
    printf("unicode filename(LE): \n");
    for(i = 0; i < unicode_name_size; i++)
        printf("%02x", file_name_LE[i]);
    printf("\n");

    p_src = (UINT16 *)file_name_LE;

    strlen_filename_only = lv_txt_unicode_2_utf8(p_src, unicode_name_size, filename_only);
    printf("utf8 filename len: %d\n", strlen_filename_only);
    if(strlen_filename_only > 250)
        printf("Warning!! utf8 filename out of array bound, %d\n", strlen_filename_only);

    printf("utf8 filename: \n");
    for(i = 0; i < strlen_filename_only; i++)
        printf("%x", filename_only[i]);
    printf("\n");
}
#endif

static VOID bt_file_send_malloc(VOID)
{
    if(NULL == bt_send_context) {
        bt_send_context = (bt_file_send_context *)lv_mem_alloc(sizeof(bt_file_send_context));
        memset(bt_send_context, 0, sizeof(bt_file_send_context));
    }
    LV_ASSERT_MEM(bt_send_context);

    if(NULL == bt_obex_chunk) {
        bt_obex_chunk = (struct bt_task_obex_file_send *)lv_mem_alloc(sizeof(struct bt_task_obex_file_send));
        memset(bt_obex_chunk, 0, sizeof(struct bt_task_obex_file_send));
    }
    LV_ASSERT_MEM(bt_obex_chunk);
}

static VOID bt_file_send_free(VOID)
{
    if(NULL != bt_send_context) {
        if(NULL != bt_send_context->fd.file_d)
            lv_fs_close(&bt_send_context->fd);
        if(NULL != bt_send_context->chunk_buffer) {
            lv_mem_free(bt_send_context->chunk_buffer);
            bt_send_context->chunk_buffer = NULL;
        }
        lv_mem_free(bt_send_context);
        bt_send_context = NULL;
    }
    if(NULL != bt_obex_chunk) {
        lv_mem_free(bt_obex_chunk);
        bt_obex_chunk = NULL;
    }
}

#ifndef USE_LV_PHONE
static INT8 * bt_get_extract_file_name(INT8 * path)
{
    INT16 i, length;
    length = (INT16)strlen(path);
    i = length - 1;
    while(i >= 0) {
        if((path[i] == '/' || path[i] == '\\') && (i != length - 1))
            break;
        i--;
    }
    if(i < 0)
        return NULL;

    return &path[(i + 1)];
}

static VOID bt_send_file_check_type(INT8 * file_name)
{
    INT8 expand_name[5];
    UINT8 i = 0;
    memcpy(expand_name, lv_fs_get_ext(file_name), 5);
    for(i = 0; expand_name[i]; i++)
        expand_name[i] = tolower(expand_name[i]);
    printf("expand name: %s\n", expand_name);

    // text
    if(strcmp(expand_name, "txt") == 0) {
        printf("file_type: text/plain\n");
        snprintf((char *)bt_obex_chunk->type, MAX_BT_OBEX_FILE_TYPE_SIZE, "%s", "text/plain");
    }
    // picture
    else if((strcmp(expand_name, "jpg") == 0)
            || (strcmp(expand_name, "jpe") == 0)
            || (strcmp(expand_name, "jpeg") == 0)) {
        printf("file_type: image/jpeg\n");
        snprintf((char *)bt_obex_chunk->type, MAX_BT_OBEX_FILE_TYPE_SIZE, "%s", "image/jpeg");
    } else if(strcmp(expand_name, "bmp") == 0) {
        printf("file_type: image/bmp\n");
        snprintf((char *)bt_obex_chunk->type, MAX_BT_OBEX_FILE_TYPE_SIZE, "%s", "image/bmp");
    } else if(strcmp(expand_name, "gif") == 0) {
        printf("file_type: image/gif\n");
        snprintf((char *)bt_obex_chunk->type, MAX_BT_OBEX_FILE_TYPE_SIZE, "%s", "image/gif");
    }
    // audio
    else if(strcmp(expand_name, "mp3") == 0) {
        printf("file_type: audio/mpeg\n");
        snprintf((char *)bt_obex_chunk->type, MAX_BT_OBEX_FILE_TYPE_SIZE, "%s", "audio/mpeg");
    } else if(strcmp(expand_name, "wav") == 0) {
        printf("file_type: audio/x-wav\n");
        snprintf((char *)bt_obex_chunk->type, MAX_BT_OBEX_FILE_TYPE_SIZE, "%s", "audio/x-wav");
    } else if((strcmp(expand_name, "mid") == 0)
              || (strcmp(expand_name, "midi") == 0)) {
        printf("file_type: audio/mid\n");
        snprintf((char *)bt_obex_chunk->type, MAX_BT_OBEX_FILE_TYPE_SIZE, "%s", "audio/mid");
    }
    // video
    else if(strcmp(expand_name, "avi") == 0) {
        printf("file_type: video/x-msvideo\n");
        snprintf((char *)bt_obex_chunk->type, MAX_BT_OBEX_FILE_TYPE_SIZE, "%s", "video/x-msvideo");
    } else {
        printf("unsupport %s, set to: text/plain\n", expand_name);
        snprintf((char *)bt_obex_chunk->type, MAX_BT_OBEX_FILE_TYPE_SIZE, "%s", "text/plain");
    }
}

static VOID bt_send_file(const INT8 * file_name)
{
    UINT32 strlen_filename_only = 0;
    UINT32 remaind_file_size, remaind_chunk_size;
    UINT32 file_size;
    UINT32 readlen;
    UINT32 * pBuf;
    INT8 * filename_only = NULL;
    UINT32 ret = 0;
    UINT32 i = 0;

    bt_send_context->chunk_buffer = (UINT8 *)lv_mem_alloc(FS_MAX_CHUNK_SIZE);
    if(bt_send_context->chunk_buffer == NULL) {
        printf("malloc fs chunk failed\n");
        bt_file_send_cancel();
        return;
    }

    lv_fs_file_t fd;
    lv_fs_res_t res = lv_fs_open(&fd, file_name, LV_FS_MODE_RD);
    if(res != LV_FS_RES_OK) {
        printf("%s open failed\n", file_name);
        bt_file_send_cancel();
        return;
    }

    lv_fs_size(&fd, &file_size);
    filename_only = bt_get_extract_file_name((INT8 *)file_name);
    strlen_filename_only = strlen(filename_only);

    printf("bt_send_file(%s), %s, file size=0x%x(%d)\n",
           filename_only, file_name, file_size, file_size);
    printf("utf8 filename len: %d\n", strlen_filename_only);
    printf("utf8 filename: \n");
    for(i = 0; i < strlen_filename_only; i++)
        printf("%x", filename_only[i]);
    printf("\n");
    printf("chunk buffer: 0x%x, max chunk size: 0x%x(%d), frame size: 0x%x(%d)\n",
           bt_send_context->chunk_buffer, FS_MAX_CHUNK_SIZE, FS_MAX_CHUNK_SIZE, BT_OBEX_FRAME_SIZE, BT_OBEX_FRAME_SIZE);

    memcpy(&bt_send_context->fd, &fd, sizeof(lv_fs_file_t));
    bt_send_context->file_size = file_size;
    bt_send_context->file_offset = 0;
    bt_send_context->chunk_offset = 0;

    remaind_file_size = bt_send_context->file_size - bt_send_context->file_offset;
    bt_send_context->chunk_size = (remaind_file_size <= FS_MAX_CHUNK_SIZE) ? remaind_file_size : FS_MAX_CHUNK_SIZE;

    res = lv_fs_read(&fd, bt_send_context->chunk_buffer, bt_send_context->chunk_size, &readlen);
    if(res != LV_FS_RES_OK) {
        printf("read file failed\n");
        lv_fs_close(&fd);
        bt_file_send_cancel();
        return;
    }

    bt_firstput = TRUE;

    printf("fs read 0x%x(%d) bytes to chunk buffer 0x%x\n", bt_send_context->chunk_size, bt_send_context->chunk_size, bt_send_context->chunk_buffer);
    pBuf = (UINT32 *)bt_send_context->chunk_buffer;
    printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", pBuf[0], pBuf[1], pBuf[2], pBuf[3]);
    printf("0x%08x, 0x%08x, 0x%08x, 0x%08x\n", pBuf[4], pBuf[5], pBuf[6], pBuf[7]);

    bt_obex_chunk->tid = bt_current_tid;
    bt_obex_chunk->total =  bt_send_context->file_size;
    bt_obex_chunk->data = bt_send_context->chunk_buffer + bt_send_context->chunk_offset;

    remaind_chunk_size = bt_send_context->chunk_size - bt_send_context->chunk_offset;
    bt_obex_chunk->payload_size = (remaind_chunk_size <= BT_OBEX_FRAME_SIZE) ?  remaind_chunk_size : BT_OBEX_FRAME_SIZE;
    bt_send_file_check_type(filename_only);

    UINT16 unicode_name[256 * 2];
    UINT32 unicode_name_size;

    memset(unicode_name, 0, 256 * 2);

    unicode_name_size = lv_txt_utf8_2_unicode((const INT8 *)filename_only, strlen_filename_only, unicode_name);
    if(unicode_name_size > 256 * 2)
        printf("Warning!! file name out of array bound, %d\n", unicode_name_size);

    bt_obex_chunk->name_size = unicode_name_size < MAX_BT_OBEX_FILE_NAME ? unicode_name_size : MAX_BT_OBEX_FILE_NAME;
    memcpy(bt_obex_chunk->name, unicode_name, bt_obex_chunk->name_size);
    bt_obex_chunk->name[bt_obex_chunk->name_size] = '\0';
    bt_obex_chunk->name_size = bt_obex_chunk->name_size == unicode_name_size ? unicode_name_size + 2 : MAX_BT_OBEX_FILE_NAME;

    printf("unicode filename len: %d\n", bt_obex_chunk->name_size);
    printf("unicode filename: \n");
    for(i = 0; i < bt_obex_chunk->name_size; i++)
        printf("%02x", bt_obex_chunk->name[i]);
    printf("\n");

    bt_obex_chunk->final = (bt_obex_chunk->payload_size == remaind_file_size) ? 1 : 0;

    printf("prepare new chunk, data=0x%x, chunk size=0x%x(%d)\n", bt_obex_chunk->data, bt_send_context->chunk_size, bt_send_context->chunk_size);

    printf("start: data=0x%x, payload size=0x%x(%d), final=%d\n", bt_obex_chunk->data, bt_obex_chunk->payload_size, bt_obex_chunk->payload_size, bt_obex_chunk->final);
    printf("call appbt_obex_send_file()++, tid: %d\n", bt_current_tid);
    ret = appbt_obex_send_file(bt_obex_chunk);
    printf("call appbt_obex_send_file()--, ret: %d\n", ret);
}
#endif

static VOID bt_send_connect_message_to_ui(BOOL connect, BOOL for_audio)
{
    mmi_msg_bt_connect_response_t * msg = (mmi_msg_bt_connect_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_connect_response_t));
    msg->header.MsgId = MMI_BT_HEADSET_CONNECT_CNF;
    msg->connect = connect;
    msg->for_audio = for_audio;
    Hal_Send_Message((TASK_ID)gui_task_id, (VOID *)msg);
}

static BOOL bt_connect_auto(struct bt_addr addr)
{
    UINT8 i = 0;
    BOOL b_find = false;
    for(i = 0; i < bt_paired_dev_cnt; i++) {
        if(memcmp(addr.bytes, bt_paired_list[i].addr, 6) == 0) {
            printf("bt dev %s is already in pairedlist, can't add scanlist! ,auto_connect:%d bt_headset_connect:%d\n",
                   bt_paired_list[i].name, bt_paired_list[i].auto_connect, bt_headset_connect);
            if((bt_paired_list[i].cod & COD_TYPE_HEADSET) == COD_TYPE_HEADSET
                    && bt_paired_list[i].auto_connect && !bt_headset_connect) {
                if(bt_inquiry_cur_state == BT_INQUIRY_INQUIRYING) {
                    #if USE_LV_WATCH_DEMO != 0
                    if(bluetooth_setting_is_active()) {
                    #endif
                    #if USE_LV_PHONE != 0
                    if(Bluetooth_Setting_Is_Top()) {
                    #endif
                        bt_inquiry_cur_state = BT_INQUIRY_INTERRUPT;
                    }
                }
                if(appbt_connect_headset(addr, 0) == 0) {
                    bt_paired_list[i].profile_status[PROFILE_CONNECTION] = PROFILE_STATE_CONNECTING;
                }
            }
            b_find = true;
            break;
        }
    }
    return b_find;
}

static VOID bt_handle_common(struct bt_task_event * event)
{
    printf("bt+++ bluetooth_handle_common %d\n", event->event_id);
    switch(event->event_id) {
        case BTTASK_IND_POWERUP_COMPLETE: {
            bt_opening = false;
            bt_open_res = true;
            #if USE_LV_BLE_DEMO
            ble_init();
            #endif
#if USE_LV_WLAN
            if(bt_fw_assert && hal_wlan_wait_for_reopen_for_hw_reset()) {
                break;
            }
#endif
            #if USE_LV_WATCH_DEMO != 0
            if(!bluetooth_setting_is_active())
            #endif
            #if USE_LV_PHONE != 0
            if(!Bluetooth_Setting_Is_Top())
            #endif
                bt_inquiry_device();
#if USE_LV_WLAN
            hal_wlan_proc_continue();
#endif
            break;
        }
        case BTTASK_IND_POWERUP_FAILED: {
            bt_open_res = false;
            bt_close_device();
#if USE_LV_WLAN
            hal_wlan_proc_continue();
#endif
            break;
        }
        case BTTASK_IND_SHUTDOWN_COMPLETE: {
            if(bt_opening)
                bt_opening = false;
            if(acl_handle) {
                memset(acl_addr.bytes, 0, 6);
                acl_handle = 0;
            }
            for(uint8_t i = 0; i < bt_paired_dev_cnt; i++) {
                if(bt_paired_list[i].profile_status[PROFILE_CONNECTION] == PROFILE_STATE_CONNECTING) {
                    bt_paired_list[i].profile_status[PROFILE_CONNECTION] = PROFILE_STATE_DISCONNECTED;
                }
            }
            break;
        }
        case BTTASK_IND_INQUIRY_RESULT: {
            struct bt_event_inquiry * inquiry = (struct bt_event_inquiry *)event->payload;
            struct bt_addr addr;
            memcpy(addr.bytes, inquiry->addr, 6);
            if(!bt_connect_auto(addr))
                bt_add_scan_list(inquiry);
            break;

        }
        case BTTASK_IND_INQUIRY_COMPLETE: {
            if(bt_inquiry_cur_state == BT_INQUIRY_INTERRUPT) break;
            bt_inquiry_cur_state = BT_INQUIRY_COMPLETE;
            printf("%s:bt_inquiry_cur_state = %d\n", __FUNCTION__, bt_inquiry_cur_state);
            break;
        }
        case BTTASK_IND_PAIRING_REQUEST: {
            struct bt_event_pairing_request * request = (struct bt_event_pairing_request *)event->payload;
            if((request->cod & COD_TYPE_HEADSET) == COD_TYPE_HEADSET) {
                bt_accept_bonding_device(request->addr, 1);
            } else {
                if(bt_send_context != NULL && (memcmp(bt_send_context->addr.bytes, request->addr.bytes, 6) == 0)) {
                     printf("%s:sending file accept pair!!!\n", __FUNCTION__);
                     bt_accept_bonding_device(request->addr, 1);
                     break;
                }
                if(0 == Hal_Pm_Get_State()) Hal_Pm_WakeUp();
                mmi_msg_bt_pair_response_t * msg = (mmi_msg_bt_pair_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_pair_response_t));
                msg->header.MsgId = MMI_BT_PAIR_CNF;
                msg->paired = 0;
                memcpy(msg->addr, request->addr.bytes, 6);
                bt_check_invalid_char((char*)request->name, strlen((const char*)request->name));
                memcpy(msg->name, request->name, MAX_BT_NAME);
                Hal_Send_Message((TASK_ID)gui_task_id, (VOID *)msg);
            }
            break;
        }
        case BTTASK_IND_PAIRED: {
            struct bt_event_paired * paired = (struct bt_event_paired *)event->payload;
            struct bt_addr addr;
            memcpy(addr.bytes, paired->addr, 6);
            bt_modify_paired_list(addr, PROFILE_PAIRED, PROFILE_STATE_CONNECTED);
            bt_remove_from_scan_list(addr);
            bt_save_paired_list_to_nv();
            break;
        }
        case BTTASK_IND_BTFIRMWARE_ASSERT: {
            bt_fw_assert = true;
            bt_disconnect_headset_fw_assert();
#if USE_LV_WLAN != 0
            if(hal_wlan_hw_reset_start()) {
                break;
            }
#endif
            bt_inquiry_device_cancel();
            bt_close_device();
            break;
        }
#if CONFIG_USE_BT_RLT8763BO == 1
       case BTTASK_IND_HEADSET_CONNECTED: {
           printf("RLT8763BO HEADSET CONNECTED!!");
           struct bt_addr addr;
           memcpy(addr.bytes, event->payload, 6);
           bt_modify_paired_list(addr, PROFILE_ACL, PROFILE_STATE_CONNECTED);
           bt_modify_paired_list(addr, PROFILE_PAIRED, PROFILE_STATE_CONNECTED);
           bt_remove_from_scan_list(addr);
           bt_save_paired_list_to_nv();
           bt_modify_paired_list(addr, PROFILE_A2DP, PROFILE_STATE_CONNECTED);
           bt_modify_paired_list(addr, PROFILE_A2DP_MEDIA, PROFILE_STATE_CONNECTED);
           bt_modify_paired_list(addr, PROFILE_AVRCP, PROFILE_STATE_CONNECTED);
           break;
         }
#endif
        default:
            break;
    }
}

static VOID bt_handle_a2dp(struct bt_task_event * event)
{
    printf("bt+++ bt_handle_a2dp %d\n", event->event_id);
    static struct bt_addr a2dp_addr;
    switch(event->event_id) {
        case BTTASK_IND_A2DP_CONNECTED: {
            struct bt_event_a2dp_connect * a2dp = (struct bt_event_a2dp_connect *)event->payload;
            memcpy(a2dp_addr.bytes, a2dp->addr, 6);
            bt_modify_paired_list(a2dp_addr, PROFILE_A2DP, PROFILE_STATE_CONNECTED);
            break;
        }
        case BTTASK_IND_A2DP_DISCONNECTED: {
            bt_modify_paired_list(a2dp_addr, PROFILE_A2DP, PROFILE_STATE_DISCONNECTED);
            bt_modify_paired_list(a2dp_addr, PROFILE_A2DP_MEDIA, PROFILE_STATE_DISCONNECTED);
            break;
        }
        case BTTASK_IND_A2DP_MEIDA_CONNECTED: {
            bt_modify_paired_list(a2dp_addr, PROFILE_A2DP_MEDIA, PROFILE_STATE_CONNECTED);
            break;
        }
        case BTTASK_IND_A2DP_START: {
            printf("bt+++ bt_handle_a2dp A2DP_START\n");
            break;
        }
        default:
            break;
    }
}

static VOID bt_handle_hfp(struct bt_task_event * event)
{
    printf("bt+++ bluetooth_handle_hfp %d\n", event->event_id);
    static struct bt_addr hfp_addr;
    switch(event->event_id) {
        case BTTASK_IND_HFP_CONNECTION_REQUEST: {
            break;
        }
        case BTTASK_IND_HFP_CONNECTED: {
            struct bt_event_hfp_connect * hfp = (struct bt_event_hfp_connect *)event->payload;
            memcpy(hfp_addr.bytes, hfp->addr, 6);
            printf("bt+++ hfp connect addr %02x%02x%02x%02x%02x%02x\n",
                   hfp_addr.bytes[0], hfp_addr.bytes[1], hfp_addr.bytes[2],
                   hfp_addr.bytes[3], hfp_addr.bytes[4], hfp_addr.bytes[5]);
            bt_modify_paired_list(hfp_addr, PROFILE_HFP, PROFILE_STATE_CONNECTED);
            bt_headset_hfp_connect = true;
            break;
        }
        case BTTASK_IND_HFP_DISCONNECTED: {
            bt_modify_paired_list(hfp_addr, PROFILE_HFP, PROFILE_STATE_DISCONNECTED);
            bt_headset_hfp_connect = false;
            break;
        }
        default:
            break;
    }
    bt_handle_hfp_ui(event);
}

static VOID bt_handle_avrcp(struct bt_task_event * event)
{
    printf("bt+++ bluetooth_handle_avrcp %d\n", event->event_id);
    static struct bt_addr avrcp_addr;
    switch(event->event_id) {
        case BTTASK_IND_AVRCP_CONNECTED: {
            struct bt_event_avrcp_connect  * avrcp = (struct bt_event_avrcp_connect *)event->payload;
            memcpy(avrcp_addr.bytes, avrcp->addr, 6);
            bt_modify_paired_list(avrcp_addr, PROFILE_AVRCP, PROFILE_STATE_CONNECTED);
            break;
        }
        case BTTASK_IND_AVRCP_DISCONNECTED: {
            bt_modify_paired_list(avrcp_addr, PROFILE_AVRCP, PROFILE_STATE_DISCONNECTED);
            break;
        }
        case BTTASK_IND_AVRCP_KEY_PRESSED: {
            UINT8 * id = (UINT8 *)event->payload;
            printf("bt++ avrcp KEY_PRESSED key = %02x(%s)\n", *id, bt_parse_keyvalue(*id));
            if(0 == Hal_Pm_Get_State()) {
#if USE_LV_WATCH_DEMO != 0
                watch_disable_wakeup_lcd();
#endif
                Hal_Pm_WakeUp();
            }
            mmi_msg_bt_avrcp_key_t * msg = (mmi_msg_bt_avrcp_key_t *)lv_mem_alloc(sizeof(mmi_msg_bt_avrcp_key_t));
            msg->key_state = 0;
            msg->key = *id;
            msg->header.MsgId = MMI_BT_AVRCP_KEY_CNF;
            Hal_Send_Message((TASK_ID)gui_task_id, (VOID *)msg);
            break;
        }
        case BTTASK_IND_AVRCP_KEY_RELEASED: {
            UINT8 * id = (UINT8 *)event->payload;
            printf("bt++ avrcp KEY_RELEASED key = %02x(%s)\n", *id, bt_parse_keyvalue(*id));
            mmi_msg_bt_avrcp_key_t * msg = (mmi_msg_bt_avrcp_key_t *)lv_mem_alloc(sizeof(mmi_msg_bt_avrcp_key_t));
            msg->key_state = 1;
            msg->key = *id;
            msg->header.MsgId = MMI_BT_AVRCP_KEY_CNF;
            Hal_Send_Message((TASK_ID)gui_task_id, (VOID *)msg);
            break;
        }
        default:
            break;
    }
}

static VOID bt_handle_acl(struct bt_task_event * event)
{
    printf("bt++ bt_handle_acl id:%d\n", event->event_id);
    switch(event->event_id) {
        case BTTASK_IND_ACL_CONNECT_FAILED: {
            int * reason = (int *)event->payload;
            printf("bt++ ACL connect failed reason = %02x(%s)\n", *reason, bt_parse_aclreason(*reason));
            bt_modify_paired_list(acl_addr, PROFILE_ACL, PROFILE_STATE_FAILED);
            memset(acl_addr.bytes, 0, 6);
            bt_file_send_free();
            break;
        }
        case BTTASK_IND_ACL_CONNECTED: {
            struct bt_event_acl_connect * acl = (struct bt_event_acl_connect *)event->payload;
            memcpy(acl_addr.bytes, acl->addr, 6);
            acl_handle = acl->handle;
            printf("bt+++ acl connect addr %02x%02x%02x%02x%02x%02x\n",
                   acl_addr.bytes[0], acl_addr.bytes[1], acl_addr.bytes[2],
                   acl_addr.bytes[3], acl_addr.bytes[4], acl_addr.bytes[5]);
            bt_modify_paired_list(acl_addr, PROFILE_ACL, PROFILE_STATE_CONNECTED);
            break;
        }
        case BTTASK_IND_ACL_DISCONNECTED: {
            struct bt_event_acl_disconnect * acl = (struct bt_event_acl_disconnect *)event->payload;
            printf("bt++ ACL disconnect reason reason = %02x(%s)\n", acl->reason, bt_parse_aclreason(acl->reason));
            bt_modify_paired_list(acl_addr, PROFILE_ACL, PROFILE_STATE_DISCONNECTED);
            UINT8 i = 0;
            for(i = 0; i < bt_paired_dev_cnt; i++) {
                if(memcmp(acl_addr.bytes, bt_paired_list[i].addr, 6) == 0) {
                    if(bt_paired_list[i].profile_status[PROFILE_PAIRED] != PROFILE_STATE_CONNECTED) {
                        printf("bt++ acl disconect&unpaired remove from pair list\n");
                        bt_remove_from_paired_list(acl_addr);
                        bt_save_paired_list_to_nv();
                        break;
                    } else {
                        printf("bt++ acl disconect but still paired, stay in pair list\n");
                    }
                }
            }
            memset(acl_addr.bytes, 0, 6);
            acl_handle = 0;
            bt_file_send_free();
            break;
        }
        default:
            break;
    }
}

static VOID bt_handle_common_setting(struct bt_task_event * event)
{
    printf("bt+++ bluetooth_handle_common ui %d\n", event->event_id);
    switch(event->event_id) {
        case BTTASK_IND_POWERUP_COMPLETE: {
            mmi_msg_bt_switch_response_t * msg = (mmi_msg_bt_switch_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_switch_response_t));
            msg->header.MsgId = MMI_BT_SWITCH_ON_CNF;
            msg->result = 1;
            Hal_Send_Message((TASK_ID)gui_task_id, (VOID *)msg);
            break;
        }
        case BTTASK_IND_SHUTDOWN_COMPLETE: {
            mmi_msg_bt_switch_response_t * msg = (mmi_msg_bt_switch_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_switch_response_t));
            printf("bt shutdown complete bt_open_res = %d\n", bt_open_res);
            if(bt_open_res == false) {
                msg->header.MsgId = MMI_BT_SWITCH_ON_CNF;
                msg->result = 0;
            } else {
               msg->header.MsgId = MMI_BT_SWITCH_OFF_CNF;
                msg->result = 1;
            }
            Hal_Send_Message((TASK_ID)gui_task_id, (VOID *)msg);
            break;
        }
        case BTTASK_IND_INQUIRY_RESULT: {
            mmi_msg_bt_inquiry_response_t * msg = (mmi_msg_bt_inquiry_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_inquiry_response_t));
            msg->header.MsgId = MMI_BT_INQUIRY_CNF;
            msg->complete = 0;
            Hal_Send_Message((TASK_ID)gui_task_id, (VOID *)msg);
            break;

        }
        case BTTASK_IND_INQUIRY_COMPLETE: {
            printf("bt inquiry complete ++++ bt_inquiry_cur_state = %d\n", bt_inquiry_cur_state);
            if(bt_inquiry_cur_state == BT_INQUIRY_INTERRUPT) break;
            mmi_msg_bt_inquiry_response_t * msg = (mmi_msg_bt_inquiry_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_inquiry_response_t));
            msg->header.MsgId = MMI_BT_INQUIRY_CNF;
            msg->complete = 1;
            Hal_Send_Message((TASK_ID)gui_task_id, (VOID *)msg);
            break;
        }
        case BTTASK_IND_PAIRING_REQUEST: {
            printf("bt pair request ++++\n");
            break;
        }
        case BTTASK_IND_PAIRED: {
            printf("bt paired+++++\n");
            mmi_msg_bt_pair_response_t * msg = (mmi_msg_bt_pair_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_pair_response_t));
            msg->header.MsgId = MMI_BT_PAIR_CNF;
            msg->paired = 1;
            Hal_Send_Message((TASK_ID)gui_task_id, (VOID *)msg);
            break;
        }
        default:
            break;
    }
}

static VOID bt_handle_hfp_ui(struct bt_task_event * event)
{
    void * msg = NULL;

    printf("bt+++ bluetooth_handle_hfp %d\n", event->event_id);
    if(0 == Hal_Pm_Get_State()) Hal_Pm_WakeUp();

    switch(event->event_id) {
        case BTTASK_IND_HFP_CONNECTED: {
            msg = (mmi_msg_bt_hfp_common_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_hfp_common_response_t));
            ((mmi_msg_bt_hfp_common_response_t *)msg)->header.MsgId = MMI_BT_HFP_CONNECTED;
            break;
        }
        case BTTASK_IND_HFP_DISCONNECTED: {
            msg = (mmi_msg_bt_hfp_common_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_hfp_common_response_t));
            ((mmi_msg_bt_hfp_common_response_t *)msg)->header.MsgId = MMI_BT_HFP_DISCONNECTED;
            break;
        }
        case BTTASK_IND_HFP_ATA: {
            msg = (mmi_msg_bt_hfp_common_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_hfp_common_response_t));
            ((mmi_msg_bt_hfp_common_response_t *)msg)->header.MsgId = MMI_BT_HFP_ATA;
            break;
        }
        case BTTASK_IND_HFP_ATCHUP: {
            msg = (mmi_msg_bt_hfp_common_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_hfp_common_response_t));
            ((mmi_msg_bt_hfp_common_response_t *)msg)->header.MsgId = MMI_BT_HFP_ATCHUP;
            break;
        }
        case BTTASK_IND_HFP_DIAL: {
            msg = (mmi_msg_bt_hfp_common_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_hfp_common_response_t));
            ((mmi_msg_bt_hfp_common_response_t *)msg)->header.MsgId = MMI_BT_HFP_DIAL;
            break;
        }
        case BTTASK_IND_HFP_ATCHLD: {
            msg = (mmi_msg_bt_hfp_common_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_hfp_common_response_t));
            ((mmi_msg_bt_hfp_common_response_t *)msg)->header.MsgId = MMI_BT_HFP_ATCHLD;
            break;
        }
        case BTTASK_IND_HFP_VOICE_RECOGNITION: {
            msg = (mmi_msg_bt_hfp_common_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_hfp_common_response_t));
            ((mmi_msg_bt_hfp_common_response_t *)msg)->header.MsgId = MMI_BT_HFP_VOICE_RECOGNITION;
            break;
        }
        case BTTASK_IND_HFP_SPEAKER_VOLUME: {
            msg = (mmi_msg_bt_hfp_value_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_hfp_value_response_t));
            ((mmi_msg_bt_hfp_value_response_t *)msg)->value = *(int32_t *)event->payload;
            ((mmi_msg_bt_hfp_value_response_t *)msg)->header.MsgId = MMI_BT_HFP_SPEAKER_VOLUME;
            break;
        }
        case BTTASK_IND_HFP_MICROPHONE_GAIN: {
            msg = (mmi_msg_bt_hfp_value_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_hfp_value_response_t));
            ((mmi_msg_bt_hfp_value_response_t *)msg)->value = *(int32_t *)event->payload;
            ((mmi_msg_bt_hfp_value_response_t *)msg)->header.MsgId = MMI_BT_HFP_MICROPHONE_GAIN;
            break;
        }
        default: {
            printf("%s unknow hfp event\n", __FUNCTION__);
            return;
        }
    }
    Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
}

static VOID bt_handle_acl_ui(struct bt_task_event * event)
{
    printf("bt++ bluetooth_handle_acl id:%d\n", event->event_id);
    switch(event->event_id) {
        case BTTASK_IND_ACL_CONNECT_FAILED: {
            int * reason = (int *)event->payload;
            printf("handle ACL connect failed %d\r\n", *reason);
            mmi_msg_bt_acl_response_t * msg = (mmi_msg_bt_acl_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_acl_response_t));
            msg->connect = -1;
            msg->reason = *reason;
            msg->header.MsgId = MMI_BT_ACL_CONNECT_CNF;
            Hal_Send_Message((TASK_ID)gui_task_id, (VOID *)msg);
            break;
        }
        case BTTASK_IND_ACL_DISCONNECTED: {
            struct bt_event_acl_disconnect * acl = (struct bt_event_acl_disconnect *)event->payload;
            printf("handle ACL disconnect failed %d\r\n", acl->reason);
            mmi_msg_bt_acl_response_t * msg = (mmi_msg_bt_acl_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_acl_response_t));
            msg->connect = 0;
            msg->reason = acl->reason;
            msg->header.MsgId = MMI_BT_ACL_CONNECT_CNF;
            Hal_Send_Message((TASK_ID)gui_task_id, (VOID *)msg);
            break;
        }
        default:
            break;
    }
}

static VOID bt_handle_sco_ui(struct bt_task_event * event)
{
    printf("bt++ bt_handle_sco_ui id:%d\n", event->event_id);
    switch(event->event_id) {
        case BTTASK_IND_SCO_DISCONNECTED:{
            mmi_msg_bt_hfp_common_response_t * msg = (mmi_msg_bt_hfp_common_response_t *)lv_mem_alloc(sizeof(mmi_msg_bt_hfp_common_response_t));
            msg->header.MsgId = MMI_BT_SCO_DISCONNECTED;
            Hal_Send_Message((TASK_ID)gui_task_id, (VOID *)msg);
            break;
       }
       default:
           break;
    }
}

static VOID bt_handle_event_ui(struct bt_task_event * event)
{
    switch(event->event_type) {
        case BTTASK_IND_TYPE_COMMON:
            if(bt_fw_assert) {
                if(event->event_id == BTTASK_IND_SHUTDOWN_COMPLETE) {
                    printf("%s: bt_fw_assert!! reopen bt!!\n", __FUNCTION__);
                    bt_open_device();
                } else if (event->event_id == BTTASK_IND_POWERUP_COMPLETE) {
                    bt_fw_assert = false;
                    printf("%s: bt_fw_assert!! reopen bt OK!!\n", __FUNCTION__);
#if USE_LV_WLAN != 0
                    hal_wlan_hw_reset_end();
#endif
                }
            #if USE_LV_WATCH_DEMO != 0
            } else if(bluetooth_setting_is_active()) {
            #endif
            #if USE_LV_PHONE != 0
            } else if(Bluetooth_Setting_Is_Top()) {
            #endif
                bt_handle_common_setting(event);
            }
            break;
        case BTTASK_IND_TYPE_ACL:
            bt_handle_acl_ui(event);
            break;
        case BTTASK_IND_TYPE_SCO:
            bt_handle_sco_ui(event);
            break;
        default:
            break;
    }
}

static VOID bt_handle_obex_ui(struct bt_task_event * event)
{
#if USE_LV_PHONE != 0
    printf("LV_PHONE:File transfer is not supported!!\n");
#else
    static struct bt_addr obex_addr;
    const UINT8 zero[6] = {0, 0, 0, 0, 0, 0};
    INT8 filename[256];
    INT8 filename_only[250];
    UINT8 filename_len;
    INT8 * file_dir;
    lv_fs_res_t res;
    UINT32 written_len;
    switch(event->event_id) {
        case BTTASK_IND_OBEX_OPP_SERVER_CONNECTED: {
            if(0 == Hal_Pm_Get_State()) Hal_Pm_WakeUp();
            int * tid = (int *)event->payload;
            printf("[BT OBEX]server handle connect %d\r\n", *tid);
            bt_current_tid = *tid;
            if(memcmp(&acl_addr.bytes[0], &zero[0], 6) == 0) {
                printf("BT ERROR!! acl already disconnect, wrong event: %d, %d\n", event->event_type, event->event_id);
            } else {
                memcpy(&obex_addr.bytes[0], &acl_addr.bytes[0], 6);
            }
            bt_modify_paired_list(obex_addr, PROFILE_OBEX, PROFILE_STATE_CONNECTED);

            bt_file_receive_malloc();
            bt_receive_context->disconnect_reason = BT_RECEIVE_MAX;
            break;
        }

        case BTTASK_IND_OBEX_OPP_SERVER_DISCONNECTED: {
            if(bt_receive_context) {
                printf("[BT OBEX]server handle disconnect reason = %d\r\n", bt_receive_context->disconnect_reason);
                bt_modify_paired_list(obex_addr, PROFILE_OBEX, PROFILE_STATE_DISCONNECTED);
                if(bt_receive_context->disconnect_reason >= BT_RECEIVE_NO_SDCARD) {
                    mmi_msg_bt_obex_t * msg = (mmi_msg_bt_obex_t *)lv_mem_alloc(sizeof(mmi_msg_bt_obex_t));
                    msg->header.MsgId = MMI_BT_OBEX_FILE_RECEIVE_ABORT;
                    msg->data = bt_receive_context->disconnect_reason;
                    Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
                    if(bt_receive_context->disconnect_reason == BT_RECEIVE_MAX) {
                        lv_fs_close(&(bt_receive_context->fd));
                        lv_fs_remove(bt_receive_context->file_name);
                    }
                }
#if USE_LV_WATCH_SD_AUTO_MOUNT != 0
                setting_sd_mount_app_release(CAPTURE_SD_APP_BLUETOOTH);
#else
                if(HAL_sd_user_get() && bt_receive_context->disconnect_reason != BT_RECEIVE_SD_USING) {
                    HAL_sd_user_set(false);
                }
#endif
                bt_current_tid = 0;
                bt_file_receive_free();
            } else {
                printf("[BT OBEX]server handle disconnect bt_receive_context has been freed\r\n");
            }
            break;
        }
        case BTTASK_IND_OBEX_OPP_SERVER_OBJECT_FILE_INFO: {
            struct bt_event_obex_file_info * info = (struct bt_event_obex_file_info *)event->payload;
            printf("bt file name type:%s name_size:%d, file_size:%d\n", info->file_type, info->file_name_size, info->file_size);
            if(!bt_recv_file_check_space(info->file_size)) {
                bt_file_receive_cancel();
                return;
            }
            bt_trans_filename_to_utf8(info->file_name, info->file_name_size, filename_only);
            file_dir = bt_recv_file_check_type(info->file_name, info->file_type);
            snprintf(filename, 256, "%s%s", file_dir, filename_only);
            bt_receive_context->file_size = info->file_size;
            bt_receive_context->file_offset = 0;
            printf("bt file name = %s\n", filename);
            filename_len = strlen(filename) < 255 - 1 ? strlen(filename) : 255 - 1;
            memset(bt_receive_context->file_name, 0, MAX_BT_OBEX_FILE_NAME);
            memcpy(bt_receive_context->file_name, filename, filename_len);

            res = lv_fs_open(&(bt_receive_context->fd), filename, LV_FS_MODE_WR);
            if(res != LV_FS_RES_OK) {
                bt_file_receive_cancel();
            }
            break;
        }
        case BTTASK_IND_OBEX_OPP_SERVER_OBJECT_RECIEVE_DATA: {
            if(!bt_receive_context->b_start_receive_data) {
                bt_save_1st_receive_data((UINT8 *)event->payload, event->payload_length);
                if(bt_write_1st_receive_data() == 0) {
                    mmi_msg_bt_obex_t * msg = (mmi_msg_bt_obex_t *)lv_mem_alloc(sizeof(mmi_msg_bt_obex_t));
                    if(bt_receive_context->file_offset != bt_receive_context->file_size) {
                        msg->header.MsgId = MMI_BT_OBEX_FILE_RECEIVE_START;
                    } else {
                        printf("%s: RECIEVE_DATA 1st and finish\n", __FUNCTION__);
                        msg->header.MsgId = MMI_BT_OBEX_FILE_RECEIVE_START_AND_FINISH;
                    }
                    Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
                } else {
                    bt_file_receive_cancel();
                }
                bt_free_1st_receive_data();

            } else {
                lv_fs_write(&(bt_receive_context->fd), (unsigned char *)event->payload, event->payload_length, &written_len);
                bt_receive_context->file_offset = bt_receive_context->file_offset + event->payload_length;
                printf("(%d/%d)\n", bt_receive_context->file_offset, bt_receive_context->file_size);

                if(written_len != event->payload_length)
                    printf("expected written %d bytes, written %d bytes actually\n", event->payload_length, written_len);
                bt_file_receive_continue();

                if(bt_receive_context->file_offset != bt_receive_context->file_size) {
                    mmi_msg_bt_obex_t * msg = (mmi_msg_bt_obex_t *)lv_mem_alloc(sizeof(mmi_msg_bt_obex_t));
                    msg->header.MsgId = MMI_BT_OBEX_FILE_RECEIVING;
                    Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
                }
            }

            break;
        }
        case BTTASK_IND_OBEX_OPP_SERVER_OBJECT_RECIEVE_FINISH: {
            uint16_t MsgId = MMI_BT_OBEX_FILE_RECEIVE_FINISH;
            struct bt_event_obex_receive_finish * finish_data = (struct bt_event_obex_receive_finish *)event->payload;
            if(finish_data) {
                printf("%s: RECIEVE_FINISH, file_name_size %d, file_size %d, data_len %d\n",
                       __FUNCTION__, finish_data->file_info.file_name_size,
                       finish_data->file_info.file_size, finish_data->data_len);
                if(0 < finish_data->file_info.file_name_size) {
                    if(0 == finish_data->data_len) {
                        printf("%s: RECIEVE_FINISH, no file data\n", __FUNCTION__);
                        bt_file_receive_ok();
                        break;
                    }

                    if(!bt_recv_file_check_space(finish_data->file_info.file_size)) {
                        printf("%s: RECIEVE_FINISH, no space\n", __FUNCTION__);
                        bt_file_receive_ok();
                        break;
                    }

                    bt_trans_filename_to_utf8(finish_data->file_info.file_name,
                                              finish_data->file_info.file_name_size,
                                              filename_only);
                    file_dir = bt_recv_file_check_type(finish_data->file_info.file_name,
                                                       finish_data->file_info.file_type);
                    snprintf(filename, 256, "%s%s", file_dir, filename_only);
                    bt_receive_context->file_size = finish_data->file_info.file_size;
                    bt_receive_context->file_offset = 0;
                    printf("RECIEVE_FINISH, bt file name = %s\n", filename);
                    filename_len = strlen(filename) < 255 - 1 ? strlen(filename) : 255 - 1;
                    memcpy(bt_receive_context->file_name, filename, filename_len);

                    res = lv_fs_open(&(bt_receive_context->fd), filename, LV_FS_MODE_WR);
                    if(res != LV_FS_RES_OK) {
                        printf("%s: RECIEVE_FINISH, open file failed\n", __FUNCTION__);
                        bt_file_receive_ok();
                        break;
                    }

                    bt_save_1st_receive_data((UINT8 *)finish_data->data, finish_data->data_len);
                    if(bt_write_1st_receive_data() == 0) {
                        MsgId = MMI_BT_OBEX_FILE_RECEIVE_START_AND_FINISH;
                    } else {
                        printf("%s: RECIEVE_FINISH, rcv 1st data failed\n", __FUNCTION__);
                        bt_file_receive_ok();
                        break;
                    }
                    bt_free_1st_receive_data();
                } else if(0 < finish_data->data_len) {
                    lv_fs_write(&(bt_receive_context->fd), (unsigned char *)finish_data->data, finish_data->data_len, &written_len);
                    bt_receive_context->file_offset = bt_receive_context->file_offset + finish_data->data_len;
                    printf("RECIEVE_FINISH, (%d/%d)\n", bt_receive_context->file_offset, bt_receive_context->file_size);

                    if(written_len != finish_data->data_len)
                        printf("RECIEVE_FINISH, expected written %d bytes, written %d bytes actually\n", finish_data->data_len, written_len);

                    bt_file_receive_ok();
                } else {
                    bt_file_receive_ok();
                    bt_receive_context->disconnect_reason = BT_RECEIVE_FINISH;
                    bt_receive_context->b_start_receive_data = 0;
                    lv_fs_close(&(bt_receive_context->fd));
                    mmi_msg_bt_obex_t * msg = (mmi_msg_bt_obex_t *)lv_mem_alloc(sizeof(mmi_msg_bt_obex_t));
                    msg->header.MsgId = MsgId;
                    Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
                    break;
                }
            }
            bt_file_receive_ok();
            bt_receive_context->disconnect_reason = BT_RECEIVE_FINISH;
            bt_receive_context->b_start_receive_data = 0;
            lv_fs_close(&(bt_receive_context->fd));
            mmi_msg_bt_obex_t * msg = (mmi_msg_bt_obex_t *)lv_mem_alloc(sizeof(mmi_msg_bt_obex_t));
            msg->header.MsgId = MsgId;
            Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
            break;
        }
        case BTTASK_IND_OBEX_OPP_SERVER_OBJECT_RECIEVE_ABORT: {
            printf("BT OBEX_OPP_SERVER_OBJECT_RECIEVE_ABORT\n");
            bt_receive_context->disconnect_reason = BT_RECEIVE_ABORT;
            lv_fs_close(&(bt_receive_context->fd));
            lv_fs_remove(bt_receive_context->file_name);
            mmi_msg_bt_obex_t * msg = (mmi_msg_bt_obex_t *)lv_mem_alloc(sizeof(mmi_msg_bt_obex_t));
            msg->header.MsgId = MMI_BT_OBEX_FILE_RECEIVE_ABORT;
            Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
            appbt_acl_disconnect(acl_handle);
            break;
        }
        case BTTASK_IND_OBEX_OPP_CLIENT_CONNECTED: {
            UINT32 * tid = (UINT32 *)event->payload;
            bt_current_tid = *tid;
            int ret = 0;
            printf("OBEX_OPP_CLIENT_CONNECTED, tid: 0x%x\n", bt_current_tid);
            if(memcmp(&acl_addr.bytes[0], &zero[0], 6) == 0) {
                printf("ERROR!! acl already disconnect, wrong event: %d, %d\n", event->event_type, event->event_id);
                ret = appbt_disconnect_phone(NULL);
                printf("call appbt_disconnect_phone(NULL), ret: %d\n", ret);
                bt_file_send_free();
                return;
            } else {
                memcpy(&obex_addr.bytes[0], &acl_addr.bytes[0], 6);
            }

            if(bt_send_context)
                bt_send_file(bt_send_context->file_name);

            bt_modify_paired_list(obex_addr, PROFILE_OBEX, PROFILE_STATE_CONNECTED);

            break;
        }
        case BTTASK_IND_OBEX_OPP_CLIENT_DISCONNECTED: {
            printf("OBEX_OPP_CLIENT_DISCONNECTED\n");
            bt_modify_paired_list(obex_addr, PROFILE_OBEX, PROFILE_STATE_DISCONNECTED);
            bt_file_send_free();
            break;
        }
        case BTTASK_IND_OBEX_OPP_CLIENT_OBJECT_PUT_FAILED: {
            INT32 * error_code = (INT32 *)event->payload;
            printf("OBEX_OPP_CLIENT_OBJECT_PUT_FAILED %02x(%s)\n", *error_code, bt_parse_putfailedreason(*error_code));
            bt_file_send_free();
            mmi_msg_bt_obex_t * msg = (mmi_msg_bt_obex_t *)lv_mem_alloc(sizeof(mmi_msg_bt_obex_t));
            msg->header.MsgId = MMI_BT_OBEX_FILE_SEND_FAILED;
            Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
            appbt_acl_disconnect(acl_handle);
            break;
        }
        case BTTASK_IND_OBEX_OPP_CLIENT_OBJECT_PUT_SUCCESS: {
            printf("OBEX_OPP_CLIENT_OBJECT_PUT_SUCCESS\n");
            printf("transfered: data=0x%x, payload size=0x%x(%d), final=%d\n",
                   bt_obex_chunk->data, bt_obex_chunk->payload_size, bt_obex_chunk->payload_size, bt_obex_chunk->final);
            UINT32 remaind_file_size;
            bt_send_context->file_offset += bt_obex_chunk->payload_size;
            printf("file offset=%d\n", bt_send_context->file_offset);

            remaind_file_size = bt_send_context->file_size - bt_send_context->file_offset;
            if(remaind_file_size == 0) {
                printf("file transfer finished\n");
                bt_file_send_free();
                mmi_msg_bt_obex_t * msg = (mmi_msg_bt_obex_t *)lv_mem_alloc(sizeof(mmi_msg_bt_obex_t));
                msg->header.MsgId = MMI_BT_OBEX_FILE_SEND_SUCCESS;
                Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
            } else {
                printf("there must be something wrong, check it !!\n");
            }
            appbt_obex_disconnect(bt_current_tid);
            break;
        }
        case BTTASK_IND_OBEX_OPP_CLIENT_OBJECT_PUT_CONTINUE: {
            printf("OBEX_OPP_CLIENT_OBJECT_PUT_CONTINUE\n");
            printf("transfered: data=0x%x, payload size=0x%x(%d), final=%d\n",
                   bt_obex_chunk->data, bt_obex_chunk->payload_size, bt_obex_chunk->payload_size, bt_obex_chunk->final);

            UINT32 remaind_file_size, remaind_chunk_size;
            UINT32 readlen;

            bt_send_context->file_offset += bt_obex_chunk->payload_size;
            bt_send_context->chunk_offset += bt_obex_chunk->payload_size;
            printf("PUT_CONTINUE(%d/%d)\n", bt_send_context->file_offset, bt_send_context->file_size);

            remaind_file_size = bt_send_context->file_size - bt_send_context->file_offset;
            remaind_chunk_size = bt_send_context->chunk_size - bt_send_context->chunk_offset;

            if(remaind_chunk_size == 0) {
                bt_send_context->chunk_offset = 0;
                bt_send_context->chunk_size = (remaind_file_size <= FS_MAX_CHUNK_SIZE) ? remaind_file_size : FS_MAX_CHUNK_SIZE;

                lv_fs_res_t res = lv_fs_read(&bt_send_context->fd, bt_send_context->chunk_buffer, bt_send_context->chunk_size, &readlen);
                if(res != LV_FS_RES_OK) {
                    printf("read file failed\n");
                    bt_file_send_free();
                    mmi_msg_bt_obex_t * msg = (mmi_msg_bt_obex_t *)lv_mem_alloc(sizeof(mmi_msg_bt_obex_t));
                    msg->header.MsgId = MMI_BT_OBEX_FILE_SEND_FAILED;
                    Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
                    return;
                }

                bt_obex_chunk->tid = bt_current_tid;
                bt_obex_chunk->total = bt_send_context->file_size;
                bt_obex_chunk->data = bt_send_context->chunk_buffer + bt_send_context->chunk_offset;

                remaind_chunk_size = bt_send_context->chunk_size - bt_send_context->chunk_offset;
                bt_obex_chunk->payload_size = (remaind_chunk_size <= BT_OBEX_FRAME_SIZE) ?  remaind_chunk_size : BT_OBEX_FRAME_SIZE;
                bt_obex_chunk->final = (bt_obex_chunk->payload_size == remaind_file_size) ? 1 : 0;
                printf("prepare new chunk, data=0x%x, chunk size=0x%x(%d)\n", bt_obex_chunk->data, bt_send_context->chunk_size, bt_send_context->chunk_size);

            } else {
                bt_obex_chunk->data += bt_obex_chunk->payload_size;
                bt_obex_chunk->payload_size = (remaind_chunk_size < BT_OBEX_FRAME_SIZE) ? remaind_chunk_size : BT_OBEX_FRAME_SIZE;
                bt_obex_chunk->final = (bt_obex_chunk->payload_size == remaind_file_size) ? 1 : 0;
            }

            mmi_msg_bt_obex_t * msg = (mmi_msg_bt_obex_t *)lv_mem_alloc(sizeof(mmi_msg_bt_obex_t));
            if(bt_firstput) {
                msg->header.MsgId = MMI_BT_OBEX_FILE_SEND_START;
                bt_firstput = false;
            } else {
                msg->header.MsgId = MMI_BT_OBEX_FILE_SEND_CONTINUE;
            }
            Hal_Send_Message((TASK_ID)gui_task_id, (void *)msg);
            appbt_obex_send_file(bt_obex_chunk);
            break;
        }
        default:
            break;
    }
#endif
}

static VOID bt_free(VOID *p)
{
    if(p) {
        free(p);
    }
}

static VOID bt_adp_main(VOID * para)
{
    UI_Os_Msg_t * event_msg;
    UINT16 event_type;
    struct bt_task_event * event;

    while(bt_adp_task_id == NULL) {
        uos_sleep(10);
    }

    // get msg
    while(1) {
        // receive message
        event_msg = (UI_Os_Msg_t *)Hal_Receive_Message((TASK_ID)bt_adp_task_id, TRUE);
        event_type  = (UINT16)event_msg->MsgHeader.MsgId;
        event = (struct bt_task_event *)event_msg->pMsg;

#if USE_LV_WLAN != 0
        if(event_type == WLAN_BT_FIRMWARE_ASSERT) {
            bt_handle_fw_assert_from_wlan();
            Hal_Mem_Free(event_msg);
            continue;
        } else if(event_type == WLAN_BT_RECOVER_CONNECTION) {
            bt_connect_auto(*((struct bt_addr *)event_msg->pMsg));
            Hal_Mem_Free(event_msg->pMsg);
            Hal_Mem_Free(event_msg);
            continue;
        }
#endif

        printf("bt+++ hal_bt_adp_main event_type: %d, event_id:%d\n", event_type, event->event_id);
        switch(event_type) {
            case BTTASK_IND_TYPE_COMMON:
#if CONFIG_USE_BT_RLT8763BO == 0
                bt_usr_handle_common(event);
#endif
                bt_handle_common(event);
                break;
            case BTTASK_IND_TYPE_HFP:
                bt_usr_handle_hfp(event);
                bt_handle_hfp(event);
                break;
            case BTTASK_IND_TYPE_ACL:
                bt_usr_handle_acl(event);
                bt_handle_acl(event);
                break;
            case BTTASK_IND_TYPE_A2DP:
                bt_usr_handle_a2dp(event);
                bt_handle_a2dp(event);
                break;
            case BTTASK_IND_TYPE_AVRCP:
                bt_usr_handle_avrcp(event);
                bt_handle_avrcp(event);
                break;
            case BTTASK_IND_TYPE_OBEX:
                bt_handle_obex_ui(event);
                break;
            case BTTASK_IND_TYPE_SCO:
                printf("get sco event\n");
                bt_usr_handle_sco(event);
                break;
            case BTTASK_IND_TYPE_LE:
#if USE_LV_BLE_DEMO != 0
                bt_usr_handle_le(event);
#endif
                break;
            default:
                break;
        }
#if CONFIG_USE_BT_RLT8763BO == 0
        bt_mgr_handler(event);
#endif
        bt_handle_event_ui(event);

        bt_free(event);
        Hal_Mem_Free(event_msg);
    }
}

static VOID bt_event_handle(struct bt_task_event * event)
{
    if(bt_adp_task_id == NULL) {
        printf("usr_task_msgq == NULL\n");
        printf("the event (%d, %d) will be free\n",
               (event != NULL) ? event->event_type : -1,
               (event != NULL) ? event->event_id : -1);
        bt_free(event);
        event = NULL;
    } else {
        printf("bt+++ the event (%d, %d) will be sent to btadp\n",
               (event != NULL) ? event->event_type : -1,
               (event != NULL) ? event->event_id : -1);

        UI_Os_Msg_t * message = (UI_Os_Msg_t *)lv_mem_alloc(sizeof(UI_Os_Msg_t));
        message->MsgHeader.MsgId = (UI_INTER_MSG_ID)event->event_type;
        message->pMsg = (UI_Inter_Msg_t *)event;
        Hal_Send_Message((TASK_ID)bt_adp_task_id, message);
    }
}

static const INT8 * bt_parse_aclreason(UINT32 reason)
{
    const INT8 * reasonStr = NULL;

    switch(reason) {
        case BT_ERR_CODE_UNKNOWN_CONNECTION_IDENTIFIER:
            reasonStr = "UNKNOWN_CONNECTION_IDENTIFIER";
            break;
        case BT_ERR_CODE_HARDWARE_FAILURE:
            reasonStr = "HARDWARE_FAILURE";
            break;
        case BT_ERR_CODE_PAGE_TIMEOUT:
            reasonStr = "PAGE_TIMEOUT";
            break;
        case BT_ERR_CODE_AUTHENTICATION_FAILURE:
            reasonStr = "AUTHENTICATION_FAILURE";
            break;
        case BT_ERR_CODE_PIN_or_KEY_MISSING:
            reasonStr = "PIN_or_KEY_MISSING";
            break;
        case BT_ERR_CODE_CONNECTION_TIMEOUT:
            reasonStr = "CONNECTION_TIMEOUT";
            break;
        case BT_ERR_CODE_CONNECTION_ALREADY_EXISTS:
            reasonStr = "CONNECTION_ALREADY_EXISTS";
            break;
        case BT_ERR_CODE_CONNECTION_ACCEPT_TIMEOUT_EXCEEDED:
            reasonStr = "CONNECTION_ACCEPT_TIMEOUT_EXCEEDED";
            break;
        case BT_ERR_CODE_REMOTE_USER_TERMINATED_CONNECTION:
            reasonStr = "REMOTE_USER_TERMINATED_CONNECTION";
            break;
        case BT_ERR_CODE_CONNECTION_TERMINATED_BY_LOCAL_HOST:
            reasonStr = "CONNECTION_TERMINATED_BY_LOCAL_HOST";
            break;
        default:
            reasonStr = "UNKNOWN REASON";
            break;
    }

    return reasonStr;
}

#ifndef USE_LV_PHONE
static const INT8 * bt_parse_putfailedreason(UINT32 reason)
{
    const INT8 * reasonStr = NULL;

    switch(reason) {
        case OBEX_RESPONSE_CONTINUE:
            reasonStr = "CONTINUE";
            break;
        case OBEX_RESPONSE_OK:
            reasonStr = "OK";
            break;
        case OBEX_RESPONSE_NON_AUTH_INFORMATION:
            reasonStr = "NON_AUTH_INFORMATION";
            break;
        case OBEX_RESPONSE_NO_CONTENT:
            reasonStr = "NO_CONTENT";
            break;
        case OBEX_RESPONSE_BAD_REQUEST:
            reasonStr = "BAD_REQUEST";
            break;
        case OBEX_RESPONSE_UNAUTHORIZED:
            reasonStr = "UNAUTHORIZED";
            break;
        case OBEX_RESPONSE_FORBIDDEN:
            reasonStr = "FORBIDDEN";
            break;
        case OBEX_RESPONSE_NOT_FOUND:
            reasonStr = "NOT_FOUND";
            break;
        case OBEX_RESPONSE_METHOD_NOT_ALLOWED:
            reasonStr = "METHOD_NOT_ALLOWED";
            break;
        case OBEX_RESPONSE_NOT_ACCEPTABLE:
            reasonStr = "NOT_ACCEPTABLE";
            break;
        default:
            reasonStr = "UNKNOWN REASON";
            break;
    }

    return reasonStr;
}
#endif

const INT8 * bt_parse_keyvalue(UINT32 keycode)
{
    const INT8 * reasonStr = NULL;

    switch(keycode) {
        case AVRCP_KEY_ENTER:
            reasonStr = "ENTER";
            break;
        case AVRCP_KEY_VOLUME_UP:
            reasonStr = "UP";
            break;
        case AVRCP_KEY_VOLUME_DOWN:
            reasonStr = "DOWN";
            break;
        case AVRCP_KEY_MUTE:
            reasonStr = "MUTE";
            break;
        case AVRCP_KEY_PLAY:
            reasonStr = "PLAY";
            break;
        case AVRCP_KEY_STOP:
            reasonStr = "STOP";
            break;
        case AVRCP_KEY_PAUSE:
            reasonStr = "PAUSE";
            break;
        case AVRCP_KEY_FORWARD:
            reasonStr = "FORWARD";
            break;
        case AVRCP_KEY_BACKWARD:
            reasonStr = "BACKWARD";
            break;
        default:
            reasonStr = "UNKNOWN KEYCODE";
            break;
    }

    return reasonStr;
}

void bt_config_firmware(void)
{
    // firmware information is necessary for memory, but not for flash
    appbt_config_firmware(BT_FIRMWARE_AT_FLASH, NULL, 0, NULL, 0);
#if 0
    appbt_config_firmware(BT_FIRMWARE_AT_MEMORY,
                          (unsigned char *)build_ram_only,
                          build_ram_only_len,
                          (unsigned char *)bt_update,
                          bt_update_len);
#endif
}

#if USE_LV_WLAN != 0
bool wlan_bt_firmware_assert(void)
{
    printf("%s\n", __FUNCTION__);

    if(!bt_get_state()) {
        return false;
    }

    UI_Os_Msg_t * message = (UI_Os_Msg_t *)lv_mem_alloc(sizeof(UI_Os_Msg_t));
    message->MsgHeader.MsgId = WLAN_BT_FIRMWARE_ASSERT;
    message->pMsg = NULL;
    Hal_Send_Message((TASK_ID)bt_adp_task_id, message);

    return true;
}

bool wlan_bt_recover_connection_fw_assert(void)
{
    if(bt_fw_assert) {
        printf("%s: reopening\n", __FUNCTION__);
        return false;
    }

    for(uint8_t i = 0; i < bt_paired_dev_cnt; i++) {
        if((bt_paired_list[i].cod & COD_TYPE_HEADSET) == COD_TYPE_HEADSET
           && bt_paired_list[i].auto_connect && !bt_headset_connect) {
            printf("%s: true\n", __FUNCTION__);
            UI_Os_Msg_t * message = (UI_Os_Msg_t *)lv_mem_alloc(sizeof(UI_Os_Msg_t));
            message->MsgHeader.MsgId = WLAN_BT_RECOVER_CONNECTION;
            struct bt_addr * addr = (struct bt_addr *)lv_mem_alloc(sizeof(struct bt_addr));
            memcpy(addr->bytes, bt_paired_list[i].addr, 6);
            message->pMsg = (UI_Inter_Msg_t *)addr;
            Hal_Send_Message((TASK_ID)bt_adp_task_id, message);
            return true;
        }
    }
    printf("%s: false\n", __FUNCTION__);
    return false;
}

static void bt_handle_fw_assert_from_wlan(void)
{
    printf("%s: bt_fw_assert %d\n", __FUNCTION__, bt_fw_assert);

    bt_fw_assert = true;
    bt_inquiry_device_cancel();
    bt_close_device();
}
#endif

#endif/*USE_LV_BLUETOOTH*/
